@Override
  protected void assignToArray(
      Expression orig, Expression receiver, Expression index, Expression rhsValueLoader) {
    ClassNode current = getController().getClassNode();
    ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current);
    ClassNode arrayComponentType = arrayType.getComponentType();
    int operationType = getOperandType(arrayComponentType);
    BinaryExpressionWriter bew = binExpWriter[operationType];
    AsmClassGenerator acg = getController().getAcg();

    if (bew.arraySet(true) && arrayType.isArray()) {
      OperandStack operandStack = getController().getOperandStack();

      // load the array
      receiver.visit(acg);
      operandStack.doGroovyCast(arrayType);

      // load index
      index.visit(acg);
      operandStack.doGroovyCast(int_TYPE);

      // load rhs
      rhsValueLoader.visit(acg);
      operandStack.doGroovyCast(arrayComponentType);

      // store value in array
      bew.arraySet(false);

      // load return value && correct operand stack stack
      operandStack.remove(3);
      rhsValueLoader.visit(acg);
    } else {
      super.assignToArray(orig, receiver, index, rhsValueLoader);
    }
  }
  private void evaluateElvisOperatorExpression(ElvisOperatorExpression expression) {
    MethodVisitor mv = controller.getMethodVisitor();
    CompileStack compileStack = controller.getCompileStack();
    OperandStack operandStack = controller.getOperandStack();
    TypeChooser typeChooser = controller.getTypeChooser();

    Expression boolPart = expression.getBooleanExpression().getExpression();
    Expression falsePart = expression.getFalseExpression();

    ClassNode truePartType = typeChooser.resolveType(boolPart, controller.getClassNode());
    ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
    ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);

    // x?:y is equal to x?x:y, which evals to
    //      var t=x; boolean(t)?t:y
    // first we load x, dup it, convert the dupped to boolean, then
    // jump depending on the value. For true we are done, for false we
    // have to load y, thus we first remove x and then load y.
    // But since x and y may have different stack lengths, this cannot work
    // Thus we have to have to do the following:
    // Be X the type of x, Y the type of y and S the common supertype of
    // X and Y, then we have to see x?:y as
    //      var t=x;boolean(t)?S(t):S(y)
    // so we load x, dup it, store the value in a local variable (t), then
    // do boolean conversion. In the true part load t and cast it to S,
    // in the false part load y and cast y to S

    // load x, dup it, store one in $t and cast the remaining one to boolean
    int mark = operandStack.getStackLength();
    boolPart.visit(controller.getAcg());
    operandStack.dup();
    if (ClassHelper.isPrimitiveType(truePartType)
        && !ClassHelper.isPrimitiveType(operandStack.getTopOperand())) {
      truePartType = ClassHelper.getWrapper(truePartType);
    }
    int retValueId = compileStack.defineTemporaryVariable("$t", truePartType, true);
    operandStack.castToBool(mark, true);

    Label l0 = operandStack.jump(IFEQ);
    // true part: load $t and cast to S
    operandStack.load(truePartType, retValueId);
    operandStack.doGroovyCast(common);
    Label l1 = new Label();
    mv.visitJumpInsn(GOTO, l1);

    // false part: load false expression and cast to S
    mv.visitLabel(l0);
    falsePart.visit(controller.getAcg());
    operandStack.doGroovyCast(common);

    // finish and cleanup
    mv.visitLabel(l1);
    compileStack.removeVar(retValueId);
    controller.getOperandStack().replace(common, 2);
  }
  protected boolean doPrimitiveCompare(
      ClassNode leftType, ClassNode rightType, BinaryExpression binExp) {
    Expression leftExp = binExp.getLeftExpression();
    Expression rightExp = binExp.getRightExpression();
    int operation = binExp.getOperation().getType();

    int operationType = getOperandConversionType(leftType, rightType);
    BinaryExpressionWriter bew = binExpWriter[operationType];

    if (!bew.write(operation, true)) return false;

    AsmClassGenerator acg = getController().getAcg();
    OperandStack os = getController().getOperandStack();
    leftExp.visit(acg);
    os.doGroovyCast(bew.getNormalOpResultType());
    rightExp.visit(acg);
    os.doGroovyCast(bew.getNormalOpResultType());
    bew.write(operation, false);

    return true;
  }
  private void evaluateNormalTernary(TernaryExpression expression) {
    MethodVisitor mv = controller.getMethodVisitor();
    OperandStack operandStack = controller.getOperandStack();
    TypeChooser typeChooser = controller.getTypeChooser();

    Expression boolPart = expression.getBooleanExpression();
    Expression truePart = expression.getTrueExpression();
    Expression falsePart = expression.getFalseExpression();

    ClassNode truePartType = typeChooser.resolveType(truePart, controller.getClassNode());
    ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
    ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);

    // we compile b?x:y as
    //      boolean(b)?S(x):S(y), S = common super type of x,y
    // so we load b, do boolean conversion.
    // In the true part load x and cast it to S,
    // in the false part load y and cast y to S

    // load b and convert to boolean
    int mark = operandStack.getStackLength();
    boolPart.visit(controller.getAcg());
    operandStack.castToBool(mark, true);

    Label l0 = operandStack.jump(IFEQ);
    // true part: load x and cast to S
    truePart.visit(controller.getAcg());
    operandStack.doGroovyCast(common);
    Label l1 = new Label();
    mv.visitJumpInsn(GOTO, l1);

    // false part: load y and cast to S
    mv.visitLabel(l0);
    falsePart.visit(controller.getAcg());
    operandStack.doGroovyCast(common);

    // finish and cleanup
    mv.visitLabel(l1);
    controller.getOperandStack().replace(common, 2);
  }
  private void evaluateLogicalAndExpression(BinaryExpression expression) {
    MethodVisitor mv = controller.getMethodVisitor();
    AsmClassGenerator acg = controller.getAcg();
    OperandStack operandStack = controller.getOperandStack();

    expression.getLeftExpression().visit(acg);
    operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
    Label falseCase = operandStack.jump(IFEQ);

    expression.getRightExpression().visit(acg);
    operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
    operandStack.jump(IFEQ, falseCase);

    ConstantExpression.PRIM_TRUE.visit(acg);
    Label trueCase = new Label();
    mv.visitJumpInsn(GOTO, trueCase);

    mv.visitLabel(falseCase);
    ConstantExpression.PRIM_FALSE.visit(acg);

    mv.visitLabel(trueCase);
    operandStack.remove(1); // have to remove 1 because of the GOTO
  }
  private void evaluateLogicalOrExpression(BinaryExpression expression) {
    MethodVisitor mv = controller.getMethodVisitor();
    AsmClassGenerator acg = controller.getAcg();
    OperandStack operandStack = controller.getOperandStack();

    Label end = new Label();

    expression.getLeftExpression().visit(acg);
    operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
    Label trueCase = operandStack.jump(IFNE);

    expression.getRightExpression().visit(acg);
    operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
    Label falseCase = operandStack.jump(IFEQ);

    mv.visitLabel(trueCase);
    ConstantExpression.PRIM_TRUE.visit(acg);
    operandStack.jump(GOTO, end);

    mv.visitLabel(falseCase);
    ConstantExpression.PRIM_FALSE.visit(acg);

    mv.visitLabel(end);
  }
 @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);
   }
 }
  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);
      }
    }
  }