/**
   * Called when the peephole optimizer is finished with an instruction. This method will pass the
   * instruction through to the next IMethodBodyVisitor, and call any needed labeling methods.
   *
   * @param info The InstructionInfo that the optimizer is done with
   */
  private void finishInstruction(InstructionInfo info) {
    finisher.visitInstruction(info.getInstruction());

    for (Label l : info.getLabelCurrents()) {
      finisher.labelCurrent(l);
    }

    for (Label l : info.getLabelNexts()) {
      finisher.labelNext(l);
    }
  }
  /**
   * Delete the previous instruction(s), from the end of the peephole window up to i. Must not be
   * called with an index that falls outside of the peephole window.
   *
   * @param i the index of the previous instruction to replace. Follows the same indexing strategy
   *     as the previous() method 0 is the last instruction in the window, 1 is two instructions
   *     ago, up to PEEPHOLE_WINDOW_SIZE-1 which is the oldest instruction that can be replaced.
   */
  private void delete(int i) {
    int size = instructions.size();
    int idx = i + 1;
    if (size >= idx) {
      int realIdx = size - idx;

      assert !indexBeforeLabel(realIdx)
          : "Attempting to delete instruction sequence that spans a label";

      List<Label> labels = null;

      for (int r = size - 1; r >= realIdx; --r) {
        InstructionInfo temp = instructions.remove(r);

        if (r == size - 1) {
          // Save any label nexts from the last deleted instruction
          List<Label> labelNexts = temp.getLabelNexts();
          if (!labelNexts.isEmpty()) {
            if (labels == null) labels = new ArrayList<Label>();
            labels.addAll(labelNexts);
          }
        } else if (r == realIdx) {
          // save any label currents from the first deleted instruction
          List<Label> labelCurrents = temp.getLabelCurrents();
          if (!labelCurrents.isEmpty()) {
            if (labels == null) labels = new ArrayList<Label>();
            labels.addAll(labelCurrents);
          }
        }
      }
      if (labels != null) {
        if (labelsFromDeletedInsns == null) labelsFromDeletedInsns = labels;
        else labelsFromDeletedInsns.addAll(labels);
      }
    } else {
      assert false : "Trying to delete instructions that fall outside the peephole window";
    }
  }
  /**
   * Perform optimizations on the instructions currently held in the window. This is so we don't do
   * the optimizations until we are sure a particular instruction was or was not the target of a
   * jump. If it was a target, then we don't do the optimizations as we don't have enough
   * information to determine all the ways control might flow to that instruction.
   */
  private void processPreviousInstructions() {
    InstructionInfo last = previous(0);
    InstructionInfo secondLast = previous(1);

    // Check if we have a labelCurrent on the last instruction, or a labelNext on the previous
    // instruction
    if (last.getLabelCurrents().isEmpty() && secondLast.getLabelNexts().isEmpty()) {
      switch (last.getOpcode()) {
        case OP_convert_b:
          {
            op_convert_b(last);
            break;
          }
        case OP_convert_d:
          {
            op_convert_d(last);
            break;
          }
        case OP_convert_i:
          {
            op_convert_i(last);
            break;
          }
        case OP_convert_u:
          {
            op_convert_u(last);
            break;
          }
        case OP_convert_s:
          {
            op_convert_s(last);
            break;
          }
        case OP_getproperty:
          {
            op_getproperty(last);
            break;
          }
        case OP_iffalse:
          {
            op_iffalse(last);
            break;
          }
        case OP_iftrue:
          {
            op_iftrue(last);
            break;
          }
        case OP_pop:
          {
            op_pop(last);
            break;
          }
        case OP_nop:
          {
            delete(0);
            break;
          }
        case OP_getlocal:
          {
            OP_getlocal(last);
            break;
          }
        case OP_returnvoid:
          {
            op_returnvoid(last);
            break;
          }
        default:
          {
            break;
          }
      }
    }
  }