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
const PUSH_LIT_RE = /^PUSH(\d+|M1)$/u;
const PUSHINT_RE = /^PUSHINT(?:8|16|32|64)$/u;

export function popValue(valueStack) {
  if (valueStack.length === 0) {
    return null;
  }
  return valueStack.pop();
}

export function popMany(valueStack, count) {
  for (let index = 0; index < count; index += 1) {
    if (valueStack.length === 0) {
      break;
    }
    valueStack.pop();
  }
}

export function ensureArgValueArray(methodArgValues, methodOffset, size) {
  const current = methodArgValues.get(methodOffset) ?? [];
  while (current.length < size) {
    current.push(null);
  }
  methodArgValues.set(methodOffset, current);
  return current;
}

export function propagateCallArguments(
  methodArgValues,
  methodArgCountsByOffset,
  targetOffset,
  valueStack,
) {
  const argCount = methodArgCountsByOffset.get(targetOffset) ?? 0;
  if (argCount === 0) {
    return;
  }
  const args = [];
  const start = Math.max(0, valueStack.length - argCount);
  for (let index = valueStack.length - 1; index >= start; index -= 1) {
    args.push(valueStack[index] ?? null);
  }
  const values = ensureArgValueArray(methodArgValues, targetOffset, argCount);
  for (let index = 0; index < argCount; index += 1) {
    values[index] = mergeValues(values[index], args[index] ?? null);
  }
  popMany(valueStack, argCount);
}

export function pointerValue(target) {
  return { kind: "pointer", target };
}

export function valueToPointer(value) {
  return value?.kind === "pointer" ? value.target : null;
}

export function mergeValues(existing, next) {
  if (next === null || next === undefined) {
    return existing ?? null;
  }
  if (existing === undefined || existing === null) {
    return next;
  }
  if (existing?.kind === "pointer" && next?.kind === "pointer") {
    return existing.target === next.target ? existing : null;
  }
  return existing === next ? existing : null;
}

export function isImmediateInteger(mnemonic) {
  return PUSH_LIT_RE.test(mnemonic) || PUSHINT_RE.test(mnemonic);
}

export function integerValue(mnemonic, instruction) {
  const match = PUSH_LIT_RE.exec(mnemonic);
  if (match) {
    return { kind: "int", value: match[1] === "M1" ? -1 : Number(match[1]) };
  }
  const raw = instruction.operand?.value;
  return { kind: "int", value: Number(raw) };
}