private void doConvertAndCast(ClassNode targetType, boolean coerce) {
    int size = stack.size();
    throwExceptionForNoStackElement(size, targetType, coerce);

    ClassNode top = stack.get(size - 1);
    targetType = targetType.redirect();
    if (targetType == top) return;

    if (coerce) {
      controller.getInvocationWriter().coerce(top, targetType);
      return;
    }

    boolean primTarget = ClassHelper.isPrimitiveType(targetType);
    boolean primTop = ClassHelper.isPrimitiveType(top);

    if (primTop && primTarget) {
      // here we box and unbox to get the goal type
      if (convertPrimitive(top, targetType)) {
        replace(targetType);
        return;
      }
      box();
    } else if (primTop) {
      // top is primitive, target is not
      // so box and do groovy cast
      controller.getInvocationWriter().castToNonPrimitiveIfNecessary(top, targetType);
    } else if (primTarget) {
      // top is not primitive so unbox
      // leave that BH#doCast later
    } else {
      controller.getInvocationWriter().castToNonPrimitiveIfNecessary(top, targetType);
    }

    MethodVisitor mv = controller.getMethodVisitor();
    if (primTarget
        && !ClassHelper.boolean_TYPE.equals(targetType)
        && !primTop
        && ClassHelper.getWrapper(targetType).equals(top)) {
      BytecodeHelper.doCastToPrimitive(mv, top, targetType);
    } else {
      top = stack.get(size - 1);
      if (!WideningCategories.implementsInterfaceOrSubclassOf(top, targetType)) {
        BytecodeHelper.doCast(mv, targetType);
      }
    }
    replace(targetType);
  }
  protected void evaluateCompareExpression(
      MethodCaller compareMethod, BinaryExpression expression) {
    Expression leftExp = expression.getLeftExpression();
    TypeChooser typeChooser = controller.getTypeChooser();
    ClassNode cn = controller.getClassNode();
    ClassNode leftType = typeChooser.resolveType(leftExp, cn);
    Expression rightExp = expression.getRightExpression();
    ClassNode rightType = typeChooser.resolveType(rightExp, cn);

    boolean done = false;
    if (ClassHelper.isPrimitiveType(leftType) && ClassHelper.isPrimitiveType(rightType)) {
      BinaryExpressionMultiTypeDispatcher helper =
          new BinaryExpressionMultiTypeDispatcher(getController());
      done = helper.doPrimitiveCompare(leftType, rightType, expression);
    }

    if (!done) {
      AsmClassGenerator acg = controller.getAcg();
      OperandStack operandStack = controller.getOperandStack();

      leftExp.visit(acg);
      operandStack.box();
      rightExp.visit(acg);
      operandStack.box();

      compareMethod.call(controller.getMethodVisitor());
      ClassNode resType = ClassHelper.boolean_TYPE;
      if (compareMethod == findRegexMethod) {
        resType = ClassHelper.OBJECT_TYPE;
      }
      operandStack.replace(resType, 2);
    }
  }
  private void evaluateCompareTo(BinaryExpression expression) {
    Expression leftExpression = expression.getLeftExpression();
    AsmClassGenerator acg = controller.getAcg();
    OperandStack operandStack = controller.getOperandStack();

    leftExpression.visit(acg);
    operandStack.box();

    // if the right hand side is a boolean expression, we need to autobox
    Expression rightExpression = expression.getRightExpression();
    rightExpression.visit(acg);
    operandStack.box();

    compareToMethod.call(controller.getMethodVisitor());
    operandStack.replace(ClassHelper.Integer_TYPE, 2);
  }
 private void writeArrayGet(
     final Expression receiver,
     final Expression arguments,
     final ClassNode rType,
     final ClassNode aType) {
   OperandStack operandStack = controller.getOperandStack();
   int m1 = operandStack.getStackLength();
   // visit receiver
   receiver.visit(controller.getAcg());
   // visit arguments as array index
   arguments.visit(controller.getAcg());
   operandStack.doGroovyCast(int_TYPE);
   int m2 = operandStack.getStackLength();
   // array access
   controller.getMethodVisitor().visitInsn(AALOAD);
   operandStack.replace(rType.getComponentType(), m2 - m1);
 }
 @Override
 protected void writePostOrPrefixMethod(
     int op, String method, Expression expression, Expression orig) {
   ClassNode type =
       getController().getTypeChooser().resolveType(orig, getController().getClassNode());
   int operationType = getOperandType(type);
   BinaryExpressionWriter bew = binExpWriter[operationType];
   if (bew.writePostOrPrefixMethod(op, true)) {
     OperandStack operandStack = getController().getOperandStack();
     // at this point the receiver will be already on the stack
     operandStack.doGroovyCast(type);
     bew.writePostOrPrefixMethod(op, false);
     operandStack.replace(bew.getNormalOpResultType());
   } else {
     super.writePostOrPrefixMethod(op, method, expression, orig);
   }
 }
  private void evaluateInstanceof(BinaryExpression expression) {
    OperandStack operandStack = controller.getOperandStack();

    expression.getLeftExpression().visit(controller.getAcg());
    operandStack.box();
    Expression rightExp = expression.getRightExpression();
    ClassNode classType;
    if (rightExp instanceof ClassExpression) {
      ClassExpression classExp = (ClassExpression) rightExp;
      classType = classExp.getType();
    } else {
      throw new RuntimeException(
          "Right hand side of the instanceof keyword must be a class name, not: " + rightExp);
    }
    String classInternalName = BytecodeHelper.getClassInternalName(classType);
    controller.getMethodVisitor().visitTypeInsn(INSTANCEOF, classInternalName);
    operandStack.replace(ClassHelper.boolean_TYPE);
  }
  public void evaluateEqual(BinaryExpression expression, boolean defineVariable) {
    AsmClassGenerator acg = controller.getAcg();
    CompileStack compileStack = controller.getCompileStack();
    OperandStack operandStack = controller.getOperandStack();
    Expression rightExpression = expression.getRightExpression();
    Expression leftExpression = expression.getLeftExpression();
    ClassNode lhsType =
        controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());

    if (defineVariable
        && rightExpression instanceof EmptyExpression
        && !(leftExpression instanceof TupleExpression)) {
      VariableExpression ve = (VariableExpression) leftExpression;
      BytecodeVariable var =
          compileStack.defineVariable(
              ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false);
      operandStack.loadOrStoreVariable(var, false);
      return;
    }

    // let's evaluate the RHS and store the result
    ClassNode rhsType;
    if (rightExpression instanceof ListExpression && lhsType.isArray()) {
      ListExpression list = (ListExpression) rightExpression;
      ArrayExpression array =
          new ArrayExpression(lhsType.getComponentType(), list.getExpressions());
      array.setSourcePosition(list);
      array.visit(acg);
    } else if (rightExpression instanceof EmptyExpression) {
      rhsType = leftExpression.getType();
      loadInitValue(rhsType);
    } else {
      rightExpression.visit(acg);
    }
    rhsType = operandStack.getTopOperand();

    boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression);
    int rhsValueId;
    if (directAssignment) {
      VariableExpression var = (VariableExpression) leftExpression;
      if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) {
        // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed
        rhsType = ClassHelper.getWrapper(rhsType);
        operandStack.box();
      }

      // ensure we try to unbox null to cause a runtime NPE in case we assign
      // null to a primitive typed variable, even if it is used only in boxed
      // form as it is closure shared
      if (var.isClosureSharedVariable()
          && ClassHelper.isPrimitiveType(var.getOriginType())
          && isNull(rightExpression)) {
        operandStack.doGroovyCast(var.getOriginType());
        // these two are never reached in bytecode and only there
        // to avoid verifyerrors and compiler infrastructure hazzle
        operandStack.box();
        operandStack.doGroovyCast(lhsType);
      }
      // normal type transformation
      if (!ClassHelper.isPrimitiveType(lhsType) && isNull(rightExpression)) {
        operandStack.replace(lhsType);
      } else {
        operandStack.doGroovyCast(lhsType);
      }
      rhsType = lhsType;
      rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex();
    } else {
      rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
    }
    // TODO: if rhs is VariableSlotLoader already, then skip crating a new one
    BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType, rhsValueId, operandStack);

    // assignment for subscript
    if (leftExpression instanceof BinaryExpression) {
      BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
      if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
        assignToArray(
            expression,
            leftBinExpr.getLeftExpression(),
            leftBinExpr.getRightExpression(),
            rhsValueLoader);
      }
      compileStack.removeVar(rhsValueId);
      return;
    }

    compileStack.pushLHS(true);

    // multiple declaration
    if (leftExpression instanceof TupleExpression) {
      TupleExpression tuple = (TupleExpression) leftExpression;
      int i = 0;
      for (Expression e : tuple.getExpressions()) {
        VariableExpression var = (VariableExpression) e;
        MethodCallExpression call =
            new MethodCallExpression(
                rhsValueLoader, "getAt", new ArgumentListExpression(new ConstantExpression(i)));
        call.visit(acg);
        i++;
        if (defineVariable) {
          operandStack.doGroovyCast(var);
          compileStack.defineVariable(var, true);
          operandStack.remove(1);
        } else {
          acg.visitVariableExpression(var);
        }
      }
    }
    // single declaration
    else if (defineVariable) {
      rhsValueLoader.visit(acg);
      operandStack.remove(1);
      compileStack.popLHS();
      return;
    }
    // normal assignment
    else {
      int mark = operandStack.getStackLength();
      // to leave a copy of the rightExpression value on the stack after the assignment.
      rhsValueLoader.visit(acg);
      TypeChooser typeChooser = controller.getTypeChooser();
      ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode());
      operandStack.doGroovyCast(targetType);
      leftExpression.visit(acg);
      operandStack.remove(operandStack.getStackLength() - mark);
    }
    compileStack.popLHS();

    // return value of assignment
    rhsValueLoader.visit(acg);
    compileStack.removeVar(rhsValueId);
  }
  private boolean doAssignmentToArray(BinaryExpression binExp) {
    if (!isAssignmentToArray(binExp)) return false;
    // we need to handle only assignment to arrays combined with an operation
    // special here. e.g x[a] += b

    int operation = removeAssignment(binExp.getOperation().getType());
    ClassNode current = getController().getClassNode();

    Expression leftExp = binExp.getLeftExpression();
    ClassNode leftType = getController().getTypeChooser().resolveType(leftExp, current);
    Expression rightExp = binExp.getRightExpression();
    ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);

    int operationType = getOperandType(leftType);
    BinaryExpressionWriter bew = binExpWriter[operationType];

    boolean simulationSuccess = bew.arrayGet(LEFT_SQUARE_BRACKET, true);
    simulationSuccess = simulationSuccess && bew.write(operation, true);
    simulationSuccess = simulationSuccess && bew.arraySet(true);
    if (!simulationSuccess) return false;

    AsmClassGenerator acg = getController().getAcg();
    OperandStack operandStack = getController().getOperandStack();
    CompileStack compileStack = getController().getCompileStack();

    // for x[a] += b we have the structure:
    //   x = left(left(binExp))), b = right(binExp), a = right(left(binExp)))
    // for array set we need these values on stack: array, index, right
    // for array get we need these values on stack: array, index
    // to eval the expression we need x[a] = x[a]+b
    // -> arraySet(x,a, x[a]+b)
    // -> arraySet(x,a, arrayGet(x,a,b))
    // --> x,a, x,a, b as operands
    // --> load x, load a, DUP2, call arrayGet, load b, call operation,call arraySet
    // since we cannot DUP2 here easily we will save the subscript and DUP x
    // --> sub=a, load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call
    // arraySet

    BinaryExpression arrayWithSubscript = (BinaryExpression) leftExp;
    Expression subscript = arrayWithSubscript.getRightExpression();

    // load array index: sub=a [load x, DUP, load sub, call arrayGet, load b, call operation, load
    // sub, call arraySet]
    subscript.visit(acg);
    operandStack.doGroovyCast(int_TYPE);
    int subscriptValueId = compileStack.defineTemporaryVariable("$sub", ClassHelper.int_TYPE, true);

    // load array: load x and DUP [load sub, call arrayGet, load b, call operation, load sub, call
    // arraySet]
    arrayWithSubscript.getLeftExpression().visit(acg);
    operandStack.doGroovyCast(leftType.makeArray());
    operandStack.dup();

    // array get: load sub, call arrayGet [load b, call operation, load sub, call arraySet]
    operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
    bew.arrayGet(LEFT_SQUARE_BRACKET, false);
    operandStack.replace(leftType, 2);

    // complete rhs: load b, call operation [load sub, call arraySet]
    binExp.getRightExpression().visit(acg);
    if (!(bew instanceof BinaryObjectExpressionHelper)) {
      // in primopts we convert to the left type for supported binary operations
      operandStack.doGroovyCast(leftType);
    }
    bew.write(operation, false);

    // let us save that value for the return
    operandStack.dup();
    int resultValueId = compileStack.defineTemporaryVariable("$result", rightType, true);

    // array set: load sub, call arraySet []
    operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
    operandStack.swap();
    bew.arraySet(false);
    operandStack.remove(3); // 3 operands, the array, the index and the value!

    // load return value
    operandStack.load(rightType, resultValueId);

    // cleanup
    compileStack.removeVar(resultValueId);
    compileStack.removeVar(subscriptValueId);
    return true;
  }
  @Override
  protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) {
    int operation = removeAssignment(binExp.getOperation().getType());
    ClassNode current = getController().getClassNode();

    Expression leftExp = binExp.getLeftExpression();
    ClassNode leftTypeOrig = getController().getTypeChooser().resolveType(leftExp, current);
    ClassNode leftType = leftTypeOrig;
    Expression rightExp = binExp.getRightExpression();
    ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);

    AsmClassGenerator acg = getController().getAcg();
    OperandStack os = getController().getOperandStack();

    if (operation == LEFT_SQUARE_BRACKET) {
      leftType = leftTypeOrig.getComponentType();
      int operationType = getOperandType(leftType);
      BinaryExpressionWriter bew = binExpWriter[operationType];
      if (leftTypeOrig.isArray() && isIntCastableType(rightExp) && bew.arrayGet(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(leftTypeOrig);
        rightExp.visit(acg);
        os.doGroovyCast(int_TYPE);
        bew.arrayGet(operation, false);
        os.replace(bew.getArrayGetResultType(), 2);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    } else if (operation == DIVIDE) {
      int operationType =
          getOperandType(getController().getTypeChooser().resolveType(binExp, current));
      BinaryExpressionWriter bew = binExpWriter[operationType];
      if (bew.writeDivision(true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getDevisionOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(bew.getDevisionOpResultType());
        bew.writeDivision(false);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    } else {
      int operationType = getOperandConversionType(leftType, rightType);
      BinaryExpressionWriter bew = binExpWriter[operationType];

      if (isShiftOperation(operation)
          && isIntCastableType(rightExp)
          && bew.write(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(int_TYPE);
        bew.write(operation, false);
      } else if (bew.write(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        bew.write(operation, false);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    }
  }