public Expression transformRangeExpression(RangeExpression range) {
   final ClassNode inferred = range.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
   if (INTRANGE_TYPE.equals(inferred)) {
     ArgumentListExpression bounds =
         new ArgumentListExpression(
             new ConstantExpression(range.isInclusive(), true), range.getFrom(), range.getTo());
     ConstructorCallExpression cce = new ConstructorCallExpression(INTRANGE_TYPE, bounds);
     cce.setSourcePosition(range);
     cce.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, INTRANGE_CTOR);
     cce.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, INTRANGE_TYPE);
     return transformer.transform(cce);
   }
   return transformer.superTransform(range);
 }
 private Expression transformPropertyAssignmentToSetterCall(
     final PropertyExpression leftExpression,
     final Expression rightExpression,
     final MethodNode directMCT) {
   // transform "a.x = b" into "def tmp = b; a.setX(tmp); tmp"
   Expression arg = staticCompilationTransformer.transform(rightExpression);
   return StaticPropertyAccessHelper.transformToSetterCall(
       leftExpression.getObjectExpression(),
       directMCT,
       arg,
       false,
       false,
       false,
       true, // to be replaced with a proper test whether a return value should be used or not
       leftExpression);
 }
 private Expression convertInOperatorToTernary(
     final BinaryExpression bin,
     final Expression rightExpression,
     final Expression leftExpression) {
   MethodCallExpression call = new MethodCallExpression(rightExpression, "isCase", leftExpression);
   call.setMethodTarget(
       (MethodNode) bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
   call.setSourcePosition(bin);
   call.copyNodeMetaData(bin);
   TernaryExpression tExp =
       new TernaryExpression(
           new BooleanExpression(
               new BinaryExpression(
                   rightExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null))),
           new BinaryExpression(
               leftExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null)),
           call);
   return staticCompilationTransformer.transform(tExp);
 }
 Expression transformMethodCallExpression(final MethodCallExpression expr) {
   Expression objectExpression = expr.getObjectExpression();
   if (expr.isSafe()) {
     MethodCallExpression notSafe =
         new MethodCallExpression(objectExpression, expr.getMethod(), expr.getArguments());
     notSafe.copyNodeMetaData(expr);
     notSafe.setSpreadSafe(expr.isSpreadSafe());
     notSafe.setSourcePosition(expr);
     notSafe.setMethodTarget(expr.getMethodTarget());
     TernaryExpression texpr =
         new TernaryExpression(
             new BooleanExpression(
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol(
                         "!=",
                         objectExpression.getLineNumber(),
                         objectExpression.getColumnNumber()),
                     ConstantExpression.NULL)),
             notSafe,
             ConstantExpression.NULL);
     return staticCompilationTransformer.transform(texpr);
   }
   ClassNode type =
       staticCompilationTransformer
           .getTypeChooser()
           .resolveType(objectExpression, staticCompilationTransformer.getClassNode());
   if (type != null && type.isArray()) {
     String method = expr.getMethodAsString();
     ClassNode componentType = type.getComponentType();
     if ("getAt".equals(method)) {
       Expression arguments = expr.getArguments();
       if (arguments instanceof TupleExpression) {
         List<Expression> argList = ((TupleExpression) arguments).getExpressions();
         if (argList.size() == 1) {
           Expression indexExpr = argList.get(0);
           ClassNode argType =
               staticCompilationTransformer
                   .getTypeChooser()
                   .resolveType(indexExpr, staticCompilationTransformer.getClassNode());
           ClassNode indexType = ClassHelper.getWrapper(argType);
           if (componentType.isEnum() && ClassHelper.Number_TYPE == indexType) {
             // workaround for generated code in enums which use .next() returning a Number
             indexType = ClassHelper.Integer_TYPE;
           }
           if (argType != null && ClassHelper.Integer_TYPE == indexType) {
             BinaryExpression binaryExpression =
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
                     indexExpr);
             binaryExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType);
             return staticCompilationTransformer.transform(binaryExpression);
           }
         }
       }
     }
     if ("putAt".equals(method)) {
       Expression arguments = expr.getArguments();
       if (arguments instanceof TupleExpression) {
         List<Expression> argList = ((TupleExpression) arguments).getExpressions();
         if (argList.size() == 2) {
           Expression indexExpr = argList.get(0);
           Expression objExpr = argList.get(1);
           ClassNode argType =
               staticCompilationTransformer
                   .getTypeChooser()
                   .resolveType(indexExpr, staticCompilationTransformer.getClassNode());
           if (argType != null && ClassHelper.Integer_TYPE == ClassHelper.getWrapper(argType)) {
             BinaryExpression arrayGet =
                 new BinaryExpression(
                     objectExpression,
                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
                     indexExpr);
             arrayGet.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType);
             BinaryExpression assignment =
                 new BinaryExpression(
                     arrayGet,
                     Token.newSymbol("=", objExpr.getLineNumber(), objExpr.getColumnNumber()),
                     objExpr);
             return staticCompilationTransformer.transform(assignment);
           }
         }
       }
     }
   }
   return staticCompilationTransformer.superTransform(expr);
 }
  Expression transformBinaryExpression(final BinaryExpression bin) {
    if (bin instanceof DeclarationExpression) {
      Expression optimized = transformDeclarationExpression(bin);
      if (optimized != null) {
        return optimized;
      }
    }
    Object[] list = bin.getNodeMetaData(BINARY_EXP_TARGET);
    Token operation = bin.getOperation();
    int operationType = operation.getType();
    Expression rightExpression = bin.getRightExpression();
    Expression leftExpression = bin.getLeftExpression();
    if (bin instanceof DeclarationExpression && leftExpression instanceof VariableExpression) {
      ClassNode declarationType = ((VariableExpression) leftExpression).getOriginType();
      if (rightExpression instanceof ConstantExpression) {
        ClassNode unwrapper = ClassHelper.getUnwrapper(declarationType);
        ClassNode wrapper = ClassHelper.getWrapper(declarationType);
        if (!rightExpression.getType().equals(declarationType)
            && wrapper.isDerivedFrom(ClassHelper.Number_TYPE)
            && WideningCategories.isDoubleCategory(unwrapper)) {
          ConstantExpression constant = (ConstantExpression) rightExpression;
          if (constant.getValue() != null) {
            return optimizeConstantInitialization(
                bin, operation, constant, leftExpression, declarationType);
          }
        }
      }
    }
    if (operationType == Types.EQUAL && leftExpression instanceof PropertyExpression) {
      MethodNode directMCT =
          leftExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
      if (directMCT != null) {
        return transformPropertyAssignmentToSetterCall(
            (PropertyExpression) leftExpression, rightExpression, directMCT);
      }
    }
    if (operationType == Types.COMPARE_EQUAL || operationType == Types.COMPARE_NOT_EQUAL) {
      // let's check if one of the operands is the null constant
      CompareToNullExpression compareToNullExpression = null;
      if (isNullConstant(leftExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(rightExpression),
                operationType == Types.COMPARE_EQUAL);
      } else if (isNullConstant(rightExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(leftExpression),
                operationType == Types.COMPARE_EQUAL);
      }
      if (compareToNullExpression != null) {
        compareToNullExpression.setSourcePosition(bin);
        return compareToNullExpression;
      }
    } else if (operationType == Types.KEYWORD_IN) {
      return convertInOperatorToTernary(bin, rightExpression, leftExpression);
    }
    if (list != null) {
      if (operationType == Types.COMPARE_TO) {
        StaticTypesTypeChooser typeChooser = staticCompilationTransformer.getTypeChooser();
        ClassNode classNode = staticCompilationTransformer.getClassNode();
        ClassNode leftType = typeChooser.resolveType(leftExpression, classNode);
        if (leftType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
          ClassNode rightType = typeChooser.resolveType(rightExpression, classNode);
          if (rightType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
            Expression left = staticCompilationTransformer.transform(leftExpression);
            Expression right = staticCompilationTransformer.transform(rightExpression);
            MethodCallExpression call =
                new MethodCallExpression(left, "compareTo", new ArgumentListExpression(right));
            call.setImplicitThis(false);
            call.setMethodTarget(COMPARE_TO_METHOD);

            CompareIdentityExpression compareIdentity = new CompareIdentityExpression(left, right);
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.boolean_TYPE);
            TernaryExpression result =
                new TernaryExpression(
                    new BooleanExpression(compareIdentity), // a==b
                    CONSTANT_ZERO,
                    new TernaryExpression(
                        new BooleanExpression(new CompareToNullExpression(left, true)), // a==null
                        CONSTANT_MINUS_ONE,
                        new TernaryExpression(
                            new BooleanExpression(
                                new CompareToNullExpression(right, true)), // b==null
                            CONSTANT_ONE,
                            call)));
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.int_TYPE);
            result.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            TernaryExpression expr = (TernaryExpression) result.getFalseExpression();
            expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            expr.getFalseExpression()
                .putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            return result;
          }
        }
      }
      boolean isAssignment = StaticTypeCheckingSupport.isAssignment(operationType);
      MethodCallExpression call;
      MethodNode node = (MethodNode) list[0];
      String name = (String) list[1];
      Expression left = staticCompilationTransformer.transform(leftExpression);
      Expression right = staticCompilationTransformer.transform(rightExpression);
      BinaryExpression optimized = tryOptimizeCharComparison(left, right, bin);
      if (optimized != null) {
        optimized.removeNodeMetaData(BINARY_EXP_TARGET);
        return transformBinaryExpression(optimized);
      }
      call = new MethodCallExpression(left, name, new ArgumentListExpression(right));
      call.setImplicitThis(false);
      call.setMethodTarget(node);
      MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
      if (adapter != null) {
        ClassExpression sba =
            new ClassExpression(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
        // replace with compareEquals
        call =
            new MethodCallExpression(sba, "compareEquals", new ArgumentListExpression(left, right));
        call.setMethodTarget(adapter);
        call.setImplicitThis(false);
      }
      if (!isAssignment) return call;
      // case of +=, -=, /=, ...
      // the method represents the operation type only, and we must add an assignment
      return new BinaryExpression(
          left, Token.newSymbol("=", operation.getStartLine(), operation.getStartColumn()), call);
    }
    if (bin.getOperation().getType() == Types.EQUAL
        && leftExpression instanceof TupleExpression
        && rightExpression instanceof ListExpression) {
      // multiple assignment
      ListOfExpressionsExpression cle = new ListOfExpressionsExpression();
      boolean isDeclaration = bin instanceof DeclarationExpression;
      List<Expression> leftExpressions = ((TupleExpression) leftExpression).getExpressions();
      List<Expression> rightExpressions = ((ListExpression) rightExpression).getExpressions();
      Iterator<Expression> leftIt = leftExpressions.iterator();
      Iterator<Expression> rightIt = rightExpressions.iterator();
      if (isDeclaration) {
        while (leftIt.hasNext()) {
          Expression left = leftIt.next();
          if (rightIt.hasNext()) {
            Expression right = rightIt.next();
            BinaryExpression bexp = new DeclarationExpression(left, bin.getOperation(), right);
            bexp.setSourcePosition(right);
            cle.addExpression(bexp);
          }
        }
      } else {
        // (next, result) = [ result, next+result ]
        // -->
        // def tmp1 = result
        // def tmp2 = next+result
        // next = tmp1
        // result = tmp2
        int size = rightExpressions.size();
        List<Expression> tmpAssignments = new ArrayList<Expression>(size);
        List<Expression> finalAssignments = new ArrayList<Expression>(size);
        for (int i = 0; i < Math.min(size, leftExpressions.size()); i++) {
          Expression left = leftIt.next();
          Expression right = rightIt.next();
          VariableExpression tmpVar = new VariableExpression("$tmpVar$" + tmpVarCounter++);
          BinaryExpression bexp = new DeclarationExpression(tmpVar, bin.getOperation(), right);
          bexp.setSourcePosition(right);
          tmpAssignments.add(bexp);
          bexp = new BinaryExpression(left, bin.getOperation(), new VariableExpression(tmpVar));
          bexp.setSourcePosition(left);
          finalAssignments.add(bexp);
        }
        for (Expression tmpAssignment : tmpAssignments) {
          cle.addExpression(tmpAssignment);
        }
        for (Expression finalAssignment : finalAssignments) {
          cle.addExpression(finalAssignment);
        }
      }
      return staticCompilationTransformer.transform(cle);
    }
    return staticCompilationTransformer.superTransform(bin);
  }