/**
  * Optimizations for OP_convert_d: convert_d, convert_d -> convert_d pushbyte n, convert_d ->
  * pushdouble n pushint n, convert_d -> pushdouble n pushuint n, convert_d -> pushdouble n
  * pushdouble n, convert_d -> pushdouble n pushnan, convert_d -> pushnan lf32, convert_d -> lf32
  * lf64, convert_d -> lf64
  *
  * @param i The convert_d instruction
  */
 private void op_convert_d(InstructionInfo i) {
   InstructionInfo prev = previous(1);
   switch (prev.getOpcode()) {
     case OP_pushbyte:
       {
         // replace pushbyte, convert d with pushdouble - should be faster
         replace(
             1,
             InstructionFactory.getInstruction(
                 OP_pushdouble, new Double(convertByteImmediateToDouble(prev.getImmediate()))));
         break;
       }
     case OP_pushint:
     case OP_pushuint:
       {
         // replace pushint , convert d with pushdouble - should be faster
         replace(
             1,
             InstructionFactory.getInstruction(
                 OP_pushdouble, new Double(((Number) prev.getOperand(0)).doubleValue())));
         break;
       }
     case OP_pushdouble:
     case OP_pushnan:
     case OP_lf32:
     case OP_lf64:
     case OP_convert_d:
       {
         // result is already a double, just erase the op_convert_d
         delete(0);
         break;
       }
     default:
       {
         // nothing to do - instruction has already been added
       }
   }
 }
 /** Optimizations for OP_pop: callprop, pop -> callpropvoid callsuper, pop -> callsupervoid */
 private void op_pop(InstructionInfo i) {
   InstructionInfo prev = previous(1);
   switch (prev.getOpcode()) {
     case OP_callproperty:
       {
         replace(
             1,
             InstructionFactory.createModifiedInstruction(OP_callpropvoid, prev.getInstruction()));
         break;
       }
     case OP_callsuper:
       {
         replace(
             1,
             InstructionFactory.createModifiedInstruction(
                 OP_callsupervoid, prev.getInstruction()));
         break;
       }
     default:
       {
         // nothing to do, instruction has already been added
       }
   }
 }
 /**
  * Optimizations for OP_getproperty: findpropstrict name, getproperty name -> getlex name
  *
  * @param i the getproperty instruction
  */
 private void op_getproperty(InstructionInfo i) {
   InstructionInfo prev = previous(1);
   switch (prev.getOpcode()) {
     case OP_findpropstrict:
       {
         // can optimize findpropstrict, followed by getprop of the same name
         // if there are any instructions btwn the findpropstrict and getprop (such as to compute a
         // runtime
         // multiname), we won't match this pattern
         if (i.getOperand(0).equals(prev.getOperand(0)))
           replace(
               1, InstructionFactory.createModifiedInstruction(OP_getlex, prev.getInstruction()));
         break;
       }
   }
 }
  /** Optimizations for OP_getlocal: setlocal N, getlocal N -> dup, setlocal N */
  private void OP_getlocal(InstructionInfo i) {
    InstructionInfo prev = previous(1);
    InstructionInfo cur = previous(0);

    switch (prev.getOpcode()) {
      case OP_setlocal:
        {
          if (cur.getInstruction().getImmediate() != prev.getInstruction().getImmediate())
            break; // set,get of different locals

          Instruction[] newInsns = {
            InstructionFactory.getInstruction(OP_dup), prev.getInstruction()
          };
          replace(1, newInsns);
          break;
        }
      default:
        {
          // nothing to do, instruction has already been added
        }
    }
  }
  /**
   * Optimizations for OP_iftrue: convert_b, iftrue -> iftrue equals, iftrue -> ifeq strictequals,
   * iftrue -> ifstricteq lessthen, iftrue -> iflt lessequals, iftrue -> ifle greaterthan, iftrue ->
   * ifgt greaterequals, iftrue -> ifge pushfalse, iftrue -> jump pushtrue, iftrue -> nothing
   * strictequals, not, iftrue -> ifstrictne equals, not, iftrue -> ifne lessthan, not, iftrue ->
   * ifnlt lessequals, not, iftrue -> ifnle greaterthan, not, iftrue -> ifngt greaterequals,
   * not,iftrue -> ifnge not, iftrue -> iffalse
   *
   * @param i the iftrue instruction
   */
  private void op_iftrue(InstructionInfo i) {
    InstructionInfo prev = previous(1);

    switch (isTrueInstructionInfo(prev)) {
      case FALSE:
        delete(1);
        return;
      case TRUE:
        replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction()));
        return;
      case DONT_KNOW:
        break; // continue on if we don't know it's a constant boolean
      default:
        assert false;
    }
    switch (prev.getOpcode()) {
      case OP_convert_b:
        {
          replace(1, i.getInstruction());
          break;
        }
      case OP_equals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction()));
          break;
        }
      case OP_strictequals:
        {
          replace(
              1, InstructionFactory.createModifiedInstruction(OP_ifstricteq, i.getInstruction()));
          break;
        }
      case OP_lessthan:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction()));
          break;
        }
      case OP_lessequals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction()));
          break;
        }
      case OP_greaterthan:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction()));
          break;
        }
      case OP_greaterequals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction()));
          break;
        }

      case OP_not:
        {
          InstructionInfo prev2 = previous(2);
          switch (prev2.getOpcode()) {
            case OP_strictequals:
              {
                replace(
                    2,
                    InstructionFactory.createModifiedInstruction(
                        OP_ifstrictne, i.getInstruction()));
                break;
              }
            case OP_equals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction()));
                break;
              }
            case OP_lessthan:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction()));
                break;
              }
            case OP_lessequals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction()));
                break;
              }
            case OP_greaterthan:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction()));
                break;
              }
            case OP_greaterequals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction()));
                break;
              }
            default:
              {
                replace(
                    1,
                    InstructionFactory.createModifiedInstruction(OP_iffalse, i.getInstruction()));
                break;
              }
          }
          break;
        }

      default:
        // nothing to do, instruction was already added
    }
  }
  /**
   * Optimizations for OP_iffalse: convert_b, iffalse -> iffalse equals, iffalse -> ifne
   * strictequals, iffalse -> strictne lessthen, iffalse -> ifnlt lessequals, iffalse -> ifnle
   * greaterthan, iffalse -> ifngt greaterequals, iffalse -> ifnge pushfalse, iffalse -> jump
   * pushtrue, iffalse -> nothing strictequals, not, iffalse -> ifstrictequals equals, not, iffalse
   * -> ifeq lessthan, not, iffalse -> iflt lessequals, not, iffalse -> ifle greaterthan, not,
   * iffalse -> ifgt greaterequals, not,iffalse -> ifge not, iffalse -> iftrue
   *
   * @param i the iffalse instruction
   */
  private void op_iffalse(InstructionInfo i) {
    InstructionInfo prev = previous(1);

    // Check if we know what's on the stack. If so, we don't need
    // to do a conditional branch
    switch (isTrueInstructionInfo(prev)) {
      case TRUE:
        delete(1); // if we can't branch, just eat the push / iffalse instructions
        return;
      case FALSE:
        replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction()));
        // If we always branch, then replace the push / iffalse with an uncontidional jump
        return;
      case DONT_KNOW:
        break; // continue on if we don't know it's a constant boolean
      default:
        assert false;
    }
    switch (prev.getOpcode()) {
      case OP_convert_b:
        {
          // replace with just iffalse
          replace(1, i.getInstruction());
          break;
        }
      case OP_equals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction()));
          break;
        }
      case OP_strictequals:
        {
          replace(
              1, InstructionFactory.createModifiedInstruction(OP_ifstrictne, i.getInstruction()));
          break;
        }
      case OP_lessthan:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction()));
          break;
        }
      case OP_lessequals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction()));
          break;
        }
      case OP_greaterthan:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction()));
          break;
        }
      case OP_greaterequals:
        {
          replace(1, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction()));
          break;
        }

      case OP_not:
        {
          InstructionInfo prev2 = previous(2);
          switch (prev2.getOpcode()) {
            case OP_strictequals:
              {
                replace(
                    2,
                    InstructionFactory.createModifiedInstruction(
                        OP_ifstricteq, i.getInstruction()));
                break;
              }
            case OP_equals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction()));
                break;
              }
            case OP_lessthan:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction()));
                break;
              }
            case OP_lessequals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction()));
                break;
              }
            case OP_greaterthan:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction()));
                break;
              }
            case OP_greaterequals:
              {
                replace(
                    2, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction()));
                break;
              }
            default:
              {
                replace(
                    1, InstructionFactory.createModifiedInstruction(OP_iftrue, i.getInstruction()));
                break;
              }
          }
          break;
        }

      default:
        {
          // nothing to do, instruction was already added
        }
    }
  }
  /**
   * Helper method to invert if expr target jump other-target to if not expr other-target Used to
   * optimize some common patterns with if's and jumps
   */
  private Instruction invertIf(InstructionInfo oldIf, InstructionInfo oldJump) {
    Instruction newIf = null;
    switch (oldIf.getOpcode()) {
      case OP_ifeq:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifne, oldJump.getInstruction());
          break;
        }
      case OP_ifstricteq:
        {
          newIf =
              InstructionFactory.createModifiedInstruction(OP_ifstrictne, oldJump.getInstruction());
          break;
        }
      case OP_ifstrictne:
        {
          newIf =
              InstructionFactory.createModifiedInstruction(OP_ifstricteq, oldJump.getInstruction());
          break;
        }
      case OP_ifge:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifnge, oldJump.getInstruction());
          break;
        }
      case OP_ifgt:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifngt, oldJump.getInstruction());
          break;
        }
      case OP_iflt:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifnlt, oldJump.getInstruction());
          break;
        }
      case OP_ifle:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifnle, oldJump.getInstruction());
          break;
        }
      case OP_ifne:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifeq, oldJump.getInstruction());
          break;
        }
      case OP_ifnge:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifge, oldJump.getInstruction());
          break;
        }
      case OP_ifngt:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifgt, oldJump.getInstruction());
          break;
        }
      case OP_ifnlt:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_iflt, oldJump.getInstruction());
          break;
        }
      case OP_ifnle:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_ifle, oldJump.getInstruction());
          break;
        }
      case OP_iftrue:
        {
          newIf =
              InstructionFactory.createModifiedInstruction(OP_iffalse, oldJump.getInstruction());
          break;
        }
      case OP_iffalse:
        {
          newIf = InstructionFactory.createModifiedInstruction(OP_iftrue, oldJump.getInstruction());
          break;
        }
    }

    return newIf;
  }
 @Override
 public void visitInstruction(int opcode, Object single_operand) {
   visitInstruction(InstructionFactory.getInstruction(opcode, single_operand));
 }
 @Override
 public void visitInstruction(int opcode, Object[] operands) {
   visitInstruction(InstructionFactory.getInstruction(opcode, operands));
 }
 @Override
 public void visitInstruction(int opcode, int immediate_operand) {
   visitInstruction(InstructionFactory.getInstruction(opcode, immediate_operand));
 }
 @Override
 public void visitInstruction(int opcode) {
   visitInstruction(InstructionFactory.getInstruction(opcode));
 }