private void writePowerCall(
     Expression receiver, Expression arguments, final ClassNode rType, ClassNode aType) {
   OperandStack operandStack = controller.getOperandStack();
   int m1 = operandStack.getStackLength();
   // slow Path
   prepareSiteAndReceiver(receiver, "power", false, controller.getCompileStack().isLHS());
   operandStack.doGroovyCast(getWrapper(rType));
   visitBoxedArgument(arguments);
   operandStack.doGroovyCast(getWrapper(aType));
   int m2 = operandStack.getStackLength();
   MethodVisitor mv = controller.getMethodVisitor();
   if (BigDecimal_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
     mv.visitMethodInsn(
         INVOKESTATIC,
         "org/codehaus/groovy/runtime/DefaultGroovyMethods",
         "power",
         "(Ljava/math/BigDecimal;Ljava/lang/Integer;)Ljava/lang/Number;",
         false);
   } else if (BigInteger_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
     mv.visitMethodInsn(
         INVOKESTATIC,
         "org/codehaus/groovy/runtime/DefaultGroovyMethods",
         "power",
         "(Ljava/math/BigInteger;Ljava/lang/Integer;)Ljava/lang/Number;",
         false);
   } else if (Long_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
     mv.visitMethodInsn(
         INVOKESTATIC,
         "org/codehaus/groovy/runtime/DefaultGroovyMethods",
         "power",
         "(Ljava/lang/Long;Ljava/lang/Integer;)Ljava/lang/Number;",
         false);
   } else if (Integer_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
     mv.visitMethodInsn(
         INVOKESTATIC,
         "org/codehaus/groovy/runtime/DefaultGroovyMethods",
         "power",
         "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Number;",
         false);
   } else {
     mv.visitMethodInsn(
         INVOKESTATIC,
         "org/codehaus/groovy/runtime/DefaultGroovyMethods",
         "power",
         "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;",
         false);
   }
   controller.getOperandStack().replace(Number_TYPE, m2 - m1);
 }
 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);
 }
 private void writeStringPlusCall(
     final Expression receiver, final String message, final Expression arguments) {
   // todo: performance would be better if we created a StringBuilder
   OperandStack operandStack = controller.getOperandStack();
   int m1 = operandStack.getStackLength();
   // slow Path
   prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
   visitBoxedArgument(arguments);
   int m2 = operandStack.getStackLength();
   MethodVisitor mv = controller.getMethodVisitor();
   mv.visitMethodInsn(
       INVOKESTATIC,
       "org/codehaus/groovy/runtime/DefaultGroovyMethods",
       "plus",
       "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;",
       false);
   controller.getOperandStack().replace(STRING_TYPE, m2 - m1);
 }
 private void writeNumberNumberCall(
     final Expression receiver, final String message, final Expression arguments) {
   OperandStack operandStack = controller.getOperandStack();
   int m1 = operandStack.getStackLength();
   // slow Path
   prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
   controller.getOperandStack().doGroovyCast(Number_TYPE);
   visitBoxedArgument(arguments);
   controller.getOperandStack().doGroovyCast(Number_TYPE);
   int m2 = operandStack.getStackLength();
   MethodVisitor mv = controller.getMethodVisitor();
   mv.visitMethodInsn(
       INVOKESTATIC,
       "org/codehaus/groovy/runtime/dgmimpl/NumberNumber" + MetaClassHelper.capitalize(message),
       message,
       "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;",
       false);
   controller.getOperandStack().replace(Number_TYPE, m2 - m1);
 }
  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);
  }
  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);
  }
  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);
  }