/** Boolean generation for == with non-boolean operands */
  public void generateNonBooleanEqual(
      BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {

    boolean isEqualOperator = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
    if (((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) {
      Constant cst;
      if ((cst = this.left.constant) != Constant.NotAConstant && cst.intValue() == 0) {
        // optimized case: 0 == x, 0 != x
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
          BranchLabel falseLabel = new BranchLabel(codeStream);
          if (isEqualOperator) {
            codeStream.ifne(falseLabel);
          } else {
            codeStream.ifeq(falseLabel);
          }
          // comparison is TRUE
          codeStream.iconst_1();
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
          } else {
            BranchLabel endLabel = new BranchLabel(codeStream);
            codeStream.goto_(endLabel);
            codeStream.decrStackSize(1);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
            endLabel.place();
          }
        }
        return;
      }
      if ((cst = this.right.constant) != Constant.NotAConstant && cst.intValue() == 0) {
        // optimized case: x == 0, x != 0
        this.left.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
          BranchLabel falseLabel = new BranchLabel(codeStream);
          if (isEqualOperator) {
            codeStream.ifne(falseLabel);
          } else {
            codeStream.ifeq(falseLabel);
          }
          // comparison is TRUE
          codeStream.iconst_1();
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
          } else {
            BranchLabel endLabel = new BranchLabel(codeStream);
            codeStream.goto_(endLabel);
            codeStream.decrStackSize(1);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
            endLabel.place();
          }
        }
        return;
      }
    }

    // null cases
    if (this.right instanceof NullLiteral) {
      if (this.left instanceof NullLiteral) {
        // null == null, null != null
        if (valueRequired) {
          if (isEqualOperator) {
            codeStream.iconst_1();
          } else {
            codeStream.iconst_0();
          }
        }
      } else {
        // x == null, x != null
        this.left.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
          BranchLabel falseLabel = new BranchLabel(codeStream);
          if (isEqualOperator) {
            codeStream.ifnonnull(falseLabel);
          } else {
            codeStream.ifnull(falseLabel);
          }
          // comparison is TRUE
          codeStream.iconst_1();
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
          } else {
            BranchLabel endLabel = new BranchLabel(codeStream);
            codeStream.goto_(endLabel);
            codeStream.decrStackSize(1);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_0();
            endLabel.place();
          }
        }
      }
      return;
    } else if (this.left instanceof NullLiteral) {
      // null = x, null != x
      this.right.generateCode(currentScope, codeStream, valueRequired);
      if (valueRequired) {
        BranchLabel falseLabel = new BranchLabel(codeStream);
        if (isEqualOperator) {
          codeStream.ifnonnull(falseLabel);
        } else {
          codeStream.ifnull(falseLabel);
        }
        // comparison is TRUE
        codeStream.iconst_1();
        if ((this.bits & IsReturnedValue) != 0) {
          codeStream.generateImplicitConversion(this.implicitConversion);
          codeStream.generateReturnBytecode(this);
          // comparison is FALSE
          falseLabel.place();
          codeStream.iconst_0();
        } else {
          BranchLabel endLabel = new BranchLabel(codeStream);
          codeStream.goto_(endLabel);
          codeStream.decrStackSize(1);
          // comparison is FALSE
          falseLabel.place();
          codeStream.iconst_0();
          endLabel.place();
        }
      }
      return;
    }

    // default case
    this.left.generateCode(currentScope, codeStream, valueRequired);
    this.right.generateCode(currentScope, codeStream, valueRequired);
    if (valueRequired) {
      BranchLabel falseLabel = new BranchLabel(codeStream);
      if (isEqualOperator) {
        switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK)
            >> 4) { // operand runtime type
          case T_int:
            codeStream.if_icmpne(falseLabel);
            break;
          case T_float:
            codeStream.fcmpl();
            codeStream.ifne(falseLabel);
            break;
          case T_long:
            codeStream.lcmp();
            codeStream.ifne(falseLabel);
            break;
          case T_double:
            codeStream.dcmpl();
            codeStream.ifne(falseLabel);
            break;
          default:
            codeStream.if_acmpne(falseLabel);
        }
      } else {
        switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK)
            >> 4) { // operand runtime type
          case T_int:
            codeStream.if_icmpeq(falseLabel);
            break;
          case T_float:
            codeStream.fcmpl();
            codeStream.ifeq(falseLabel);
            break;
          case T_long:
            codeStream.lcmp();
            codeStream.ifeq(falseLabel);
            break;
          case T_double:
            codeStream.dcmpl();
            codeStream.ifeq(falseLabel);
            break;
          default:
            codeStream.if_acmpeq(falseLabel);
        }
      }
      // comparison is TRUE
      codeStream.iconst_1();
      if ((this.bits & IsReturnedValue) != 0) {
        codeStream.generateImplicitConversion(this.implicitConversion);
        codeStream.generateReturnBytecode(this);
        // comparison is FALSE
        falseLabel.place();
        codeStream.iconst_0();
      } else {
        BranchLabel endLabel = new BranchLabel(codeStream);
        codeStream.goto_(endLabel);
        codeStream.decrStackSize(1);
        // comparison is FALSE
        falseLabel.place();
        codeStream.iconst_0();
        endLabel.place();
      }
    }
  }
  /** Code generation for a binary operation */
  public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
    int pc = codeStream.position;
    if (this.constant != Constant.NotAConstant) {
      // inlined value
      if (valueRequired) codeStream.generateConstant(this.constant, this.implicitConversion);
      codeStream.recordPositionsFrom(pc, this.sourceStart);
      return;
    }
    Constant cst = this.right.constant;
    if (cst != Constant.NotAConstant) {
      // <expr> || true --> true
      if (cst.booleanValue() == true) {
        this.left.generateCode(currentScope, codeStream, false);
        if (valueRequired) codeStream.iconst_1();
      } else {
        // <expr>|| false --> <expr>
        this.left.generateCode(currentScope, codeStream, valueRequired);
      }
      if (this.mergedInitStateIndex != -1) {
        codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
      }
      codeStream.generateImplicitConversion(this.implicitConversion);
      codeStream.recordPositionsFrom(pc, this.sourceStart);
      return;
    }

    BranchLabel trueLabel = new BranchLabel(codeStream), endLabel;
    cst = this.left.optimizedBooleanConstant();
    boolean leftIsConst = cst != Constant.NotAConstant;
    boolean leftIsTrue = leftIsConst && cst.booleanValue() == true;

    cst = this.right.optimizedBooleanConstant();
    boolean rightIsConst = cst != Constant.NotAConstant;
    boolean rightIsTrue = rightIsConst && cst.booleanValue() == true;

    generateOperands:
    {
      if (leftIsConst) {
        this.left.generateCode(currentScope, codeStream, false);
        if (leftIsTrue) {
          break generateOperands; // no need to generate right operand
        }
      } else {
        this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, null, true);
        // need value, e.g. if (a == 1 || ((b = 2) > 0)) {} -> shouldn't initialize 'b' if a==1
      }
      if (this.rightInitStateIndex != -1) {
        codeStream.addDefinitelyAssignedVariables(currentScope, this.rightInitStateIndex);
      }
      if (rightIsConst) {
        this.right.generateCode(currentScope, codeStream, false);
      } else {
        this.right.generateOptimizedBoolean(
            currentScope, codeStream, trueLabel, null, valueRequired);
      }
    }
    if (this.mergedInitStateIndex != -1) {
      codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
    }
    /*
     * improving code gen for such a case: boolean b = i < 0 || true since
     * the label has never been used, we have the inlined value on the
     * stack.
     */
    if (valueRequired) {
      if (leftIsConst && leftIsTrue) {
        codeStream.iconst_1();
        codeStream.recordPositionsFrom(codeStream.position, this.left.sourceEnd);
      } else {
        if (rightIsConst && rightIsTrue) {
          codeStream.iconst_1();
          codeStream.recordPositionsFrom(codeStream.position, this.left.sourceEnd);
        } else {
          codeStream.iconst_0();
        }
        if (trueLabel.forwardReferenceCount() > 0) {
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            trueLabel.place();
            codeStream.iconst_1();
          } else {
            codeStream.goto_(endLabel = new BranchLabel(codeStream));
            codeStream.decrStackSize(1);
            trueLabel.place();
            codeStream.iconst_1();
            endLabel.place();
          }
        } else {
          trueLabel.place();
        }
      }
      codeStream.generateImplicitConversion(this.implicitConversion);
      codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    } else {
      trueLabel.place();
    }
  }
  /**
   * Boolean generation for == with boolean operands
   *
   * <p>Note this code does not optimize conditional constants !!!!
   */
  public void generateBooleanEqual(
      BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {

    // optimized cases: <something equivalent to true> == x, <something equivalent to false> == x,
    // optimized cases: <something equivalent to false> != x, <something equivalent to true> != x,
    boolean isEqualOperator = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
    Constant cst = this.left.optimizedBooleanConstant();
    if (cst != Constant.NotAConstant) {
      Constant rightCst = this.right.optimizedBooleanConstant();
      if (rightCst != Constant.NotAConstant) {
        // <something equivalent to true> == <something equivalent to true>, <something equivalent
        // to false> != <something equivalent to true>
        // <something equivalent to true> == <something equivalent to false>, <something equivalent
        // to false> != <something equivalent to false>
        this.left.generateCode(currentScope, codeStream, false);
        this.right.generateCode(currentScope, codeStream, false);
        if (valueRequired) {
          boolean leftBool = cst.booleanValue();
          boolean rightBool = rightCst.booleanValue();
          if (isEqualOperator) {
            if (leftBool == rightBool) {
              codeStream.iconst_1();
            } else {
              codeStream.iconst_0();
            }
          } else {
            if (leftBool != rightBool) {
              codeStream.iconst_1();
            } else {
              codeStream.iconst_0();
            }
          }
        }
      } else if (cst.booleanValue() == isEqualOperator) {
        // <something equivalent to true> == x, <something equivalent to false> != x
        this.left.generateCode(currentScope, codeStream, false);
        this.right.generateCode(currentScope, codeStream, valueRequired);
      } else {
        // <something equivalent to false> == x, <something equivalent to true> != x
        if (valueRequired) {
          BranchLabel falseLabel = new BranchLabel(codeStream);
          this.left.generateCode(currentScope, codeStream, false);
          this.right.generateOptimizedBoolean(
              currentScope, codeStream, null, falseLabel, valueRequired);
          // comparison is TRUE
          codeStream.iconst_0();
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_1();
          } else {
            BranchLabel endLabel = new BranchLabel(codeStream);
            codeStream.goto_(endLabel);
            codeStream.decrStackSize(1);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_1();
            endLabel.place();
          }
        } else {
          this.left.generateCode(currentScope, codeStream, false);
          this.right.generateCode(currentScope, codeStream, false);
        }
        //				left.generateCode(currentScope, codeStream, false);
        //				right.generateCode(currentScope, codeStream, valueRequired);
        //				if (valueRequired) {
        //					codeStream.iconst_1();
        //					codeStream.ixor(); // negate
        //				}
      }
      return;
    }
    cst = this.right.optimizedBooleanConstant();
    if (cst != Constant.NotAConstant) {
      if (cst.booleanValue() == isEqualOperator) {
        // x == <something equivalent to true>, x != <something equivalent to false>
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, false);
      } else {
        // x == <something equivalent to false>, x != <something equivalent to true>
        if (valueRequired) {
          BranchLabel falseLabel = new BranchLabel(codeStream);
          this.left.generateOptimizedBoolean(
              currentScope, codeStream, null, falseLabel, valueRequired);
          this.right.generateCode(currentScope, codeStream, false);
          // comparison is TRUE
          codeStream.iconst_0();
          if ((this.bits & IsReturnedValue) != 0) {
            codeStream.generateImplicitConversion(this.implicitConversion);
            codeStream.generateReturnBytecode(this);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_1();
          } else {
            BranchLabel endLabel = new BranchLabel(codeStream);
            codeStream.goto_(endLabel);
            codeStream.decrStackSize(1);
            // comparison is FALSE
            falseLabel.place();
            codeStream.iconst_1();
            endLabel.place();
          }
        } else {
          this.left.generateCode(currentScope, codeStream, false);
          this.right.generateCode(currentScope, codeStream, false);
        }
        //				left.generateCode(currentScope, codeStream, valueRequired);
        //				right.generateCode(currentScope, codeStream, false);
        //				if (valueRequired) {
        //					codeStream.iconst_1();
        //					codeStream.ixor(); // negate
        //				}
      }
      return;
    }
    // default case
    this.left.generateCode(currentScope, codeStream, valueRequired);
    this.right.generateCode(currentScope, codeStream, valueRequired);

    if (valueRequired) {
      if (isEqualOperator) {
        BranchLabel falseLabel;
        codeStream.if_icmpne(falseLabel = new BranchLabel(codeStream));
        // comparison is TRUE
        codeStream.iconst_1();
        if ((this.bits & IsReturnedValue) != 0) {
          codeStream.generateImplicitConversion(this.implicitConversion);
          codeStream.generateReturnBytecode(this);
          // comparison is FALSE
          falseLabel.place();
          codeStream.iconst_0();
        } else {
          BranchLabel endLabel = new BranchLabel(codeStream);
          codeStream.goto_(endLabel);
          codeStream.decrStackSize(1);
          // comparison is FALSE
          falseLabel.place();
          codeStream.iconst_0();
          endLabel.place();
        }
      } else {
        codeStream.ixor();
      }
    }
  }