private void prepareLeftOperand(
        BooleanOperator operation,
        Class<?> type,
        Class<?> leftType,
        Class<?> rightType,
        Label shortcutEvaluation) {
      if (leftType.isPrimitive()) {
        if (type != null) {
          castOrCoercePrimitive(LEFT_OPERAND, leftType, type);
        }
        return;
      }

      Label notNullLabel =
          jitLeftIsNull(
              type == null || leftType == type
                  ? jitNullSafeOperationStart()
                  : jitNullSafeCoercion(leftType, type));

      if (operation.isEquality() && !rightType.isPrimitive()) {
        // if (left == null) return right == null
        checkNullEquality();
      } else {
        // if (left == null) return false
        mv.visitInsn(ICONST_0);
      }

      mv.visitJumpInsn(GOTO, shortcutEvaluation);
      mv.visitLabel(notNullLabel);
    }
 private void jitMapAccessInvocation(MapAccessInvocation invocation, boolean firstInvocation) {
   if (firstInvocation) {
     mv.visitVarInsn(ALOAD, 1);
     cast(Map.class);
   }
   Class<?> keyClass = jitExpression(invocation.getKey(), invocation.getKeyType());
   if (keyClass.isPrimitive()) {
     convertPrimitiveToObject(keyClass);
   }
   invokeInterface(Map.class, "get", Object.class, Object.class);
   if (invocation.getReturnType() != Object.class) {
     cast(invocation.getReturnType());
   }
 }
    private void jitBinary(SingleCondition singleCondition) {
      Expression left = singleCondition.getLeft();
      Expression right = singleCondition.getRight();
      Class<?> commonType =
          singleCondition.getOperation().needsSameType()
              ? findCommonClass(
                  left.getType(), !left.canBeNull(), right.getType(), !right.canBeNull())
              : null;

      if (commonType == Object.class && singleCondition.getOperation().isComparison()) {
        commonType = Comparable.class;
      }

      if (commonType != null && commonType.isPrimitive()) {
        jitPrimitiveBinary(singleCondition, left, right, commonType);
      } else {
        jitObjectBinary(singleCondition, left, right, commonType);
      }
    }
    private Class<?> findCommonClass(
        Class<?> class1, boolean primitive1, Class<?> class2, boolean primitive2) {
      Class<?> result = null;
      if (class1 == class2) {
        result = class1;
      } else if (class1 == NullType.class) {
        result = convertFromPrimitiveType(class2);
      } else if (class2 == NullType.class) {
        result = convertFromPrimitiveType(class1);
      } else if (class1 == Object.class) {
        result = convertFromPrimitiveType(class2);
      } else if (class2 == Object.class) {
        result = convertFromPrimitiveType(class1);
      } else if (class1 == String.class) {
        result = convertFromPrimitiveType(class2);
      } else if (class2 == String.class) {
        result = convertFromPrimitiveType(class1);
      }

      if (result == null) {
        result = findCommonClass(class1, class2, primitive2);
      }
      if (result == null) {
        result = findCommonClass(class2, class1, primitive1);
      }
      if (result == null) {
        throw new RuntimeException(
            "Cannot find a common class between "
                + class1.getName()
                + " and "
                + class2.getName()
                + " ||  "
                + class1.hashCode()
                + " vs "
                + class2.hashCode());
      }
      return result == Number.class ? Double.class : result;
    }
 private void castOrCoercePrimitive(int regNr, Class<?> fromType, Class<?> toType) {
   if (fromType == toType) {
     return;
   }
   load(regNr);
   if (toType.isPrimitive()) {
     castPrimitiveToPrimitive(fromType, toType);
   } else {
     Class<?> toTypeAsPrimitive = convertToPrimitiveType(toType);
     castPrimitiveToPrimitive(fromType, toTypeAsPrimitive);
     invokeStatic(toType, "valueOf", toType, toTypeAsPrimitive);
   }
   store(regNr, toType);
 }
    private void prepareRightOperand(
        Expression right, Class<?> type, Class<?> rightType, Label shortcutEvaluation) {
      if (rightType.isPrimitive()) {
        if (type != null) {
          castOrCoercePrimitive(RIGHT_OPERAND, rightType, type);
        }
        return;
      }

      Label nullLabel = new Label();
      Label notNullLabel = new Label();
      load(RIGHT_OPERAND);
      mv.visitJumpInsn(IFNULL, nullLabel);
      if (type != null && !isFixed(right) && rightType != type) {
        castOrCoerceTo(RIGHT_OPERAND, rightType, type, nullLabel);
      }
      mv.visitJumpInsn(GOTO, notNullLabel);
      mv.visitLabel(nullLabel);
      mv.visitInsn(ICONST_0);
      mv.visitJumpInsn(GOTO, shortcutEvaluation);
      mv.visitLabel(notNullLabel);
    }
    private Class<?> findCommonClass(Class<?> class1, Class<?> class2, boolean canBePrimitive) {
      if (class1.isAssignableFrom(class2)) {
        return class1;
      }

      if (class1 == boolean.class && class2 == Boolean.class) {
        return canBePrimitive ? boolean.class : Boolean.class;
      }
      if (class1 == char.class && class2 == Character.class) {
        return canBePrimitive ? char.class : Character.class;
      }
      if (class1 == byte.class && class2 == Byte.class) {
        return canBePrimitive ? byte.class : Byte.class;
      }
      if (class1 == short.class && class2 == Short.class) {
        return canBePrimitive ? short.class : Short.class;
      }

      if (class1 == Number.class && class2.isPrimitive()) {
        return Double.class;
      }

      if (class1 == int.class || class1 == short.class || class1 == byte.class) {
        if (class2 == Integer.class) {
          return canBePrimitive ? int.class : Integer.class;
        }
        if (class2 == long.class) {
          return long.class;
        }
        if (class2 == Long.class) {
          return canBePrimitive ? long.class : Long.class;
        }
        if (class2 == float.class) {
          return float.class;
        }
        if (class2 == Float.class) {
          return canBePrimitive ? float.class : Float.class;
        }
        if (class2 == double.class) {
          return double.class;
        }
        if (class2 == Double.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigInteger.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == long.class) {
        if (class2 == int.class) {
          return long.class;
        }
        if (class2 == Integer.class) {
          return canBePrimitive ? long.class : Long.class;
        }
        if (class2 == Long.class) {
          return canBePrimitive ? long.class : Long.class;
        }
        if (class2 == float.class) {
          return double.class;
        }
        if (class2 == Float.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == double.class) {
          return double.class;
        }
        if (class2 == Double.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigInteger.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == float.class) {
        if (class2 == int.class) {
          return float.class;
        }
        if (class2 == Integer.class) {
          return canBePrimitive ? float.class : Float.class;
        }
        if (class2 == long.class) {
          return double.class;
        }
        if (class2 == Long.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == Float.class) {
          return canBePrimitive ? float.class : Float.class;
        }
        if (class2 == double.class) {
          return double.class;
        }
        if (class2 == Double.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigDecimal.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == double.class) {
        if (class2 == int.class) {
          return float.class;
        }
        if (class2 == Integer.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == long.class) {
          return double.class;
        }
        if (class2 == Long.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == float.class) {
          return double.class;
        }
        if (class2 == Float.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == Double.class) {
          return canBePrimitive ? double.class : Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigDecimal.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == Integer.class) {
        if (class2 == Long.class) {
          return Long.class;
        }
        if (class2 == Float.class) {
          return Float.class;
        }
        if (class2 == Double.class) {
          return Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigInteger.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == Long.class) {
        if (class2 == Float.class) {
          return Double.class;
        }
        if (class2 == Double.class) {
          return Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigInteger.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == Float.class) {
        if (class2 == Double.class) {
          return Double.class;
        }
        if (class2 == BigInteger.class) {
          return BigDecimal.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == Double.class) {
        if (class2 == BigInteger.class) {
          return BigDecimal.class;
        }
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      if (class1 == BigInteger.class) {
        if (class2 == BigDecimal.class) {
          return BigDecimal.class;
        }
      }

      return null;
    }
    private void jitObjectBinary(
        SingleCondition singleCondition, Expression left, Expression right, Class<?> type) {
      Class<?> leftType =
          isDeclarationExpression(left) ? convertFromPrimitiveType(left.getType()) : left.getType();
      Class<?> rightType =
          isDeclarationExpression(right)
              ? convertFromPrimitiveType(right.getType())
              : right.getType();

      jitExpression(left, type != null ? type : leftType);
      if (isDeclarationExpression(left) && left.getType().isPrimitive()) {
        castFromPrimitive(left.getType());
      }
      store(LEFT_OPERAND, leftType);

      jitExpression(right, type != null ? type : rightType);
      if (isDeclarationExpression(right) && right.getType().isPrimitive()) {
        castFromPrimitive(right.getType());
      }
      store(RIGHT_OPERAND, rightType);

      Label shortcutEvaluation = new Label();
      BooleanOperator operation = singleCondition.getOperation();
      prepareLeftOperand(operation, type, leftType, rightType, shortcutEvaluation);
      prepareRightOperand(right, type, rightType, shortcutEvaluation);

      load(LEFT_OPERAND);
      load(RIGHT_OPERAND);

      switch (operation) {
        case CONTAINS:
          invokeStatic(
              EvaluatorHelper.class,
              "contains",
              boolean.class,
              Object.class,
              rightType.isPrimitive() ? rightType : Object.class);
          break;
        case MATCHES:
          invokeVirtual(type, "matches", boolean.class, String.class);
          break;
        case SOUNDSLIKE:
          invokeStatic(
              EvaluatorHelper.class, "soundslike", boolean.class, String.class, String.class);
          break;
        default:
          if (operation.isEquality()) {
            if (type.isInterface()) {
              invokeInterface(type, "equals", boolean.class, Object.class);
            } else {
              invokeVirtual(type, "equals", boolean.class, Object.class);
            }
            if (operation == BooleanOperator.NE) {
              singleCondition.toggleNegation();
            }
          } else {
            if (type.isInterface()) {
              invokeInterface(
                  type, "compareTo", int.class, type == Comparable.class ? Object.class : type);
            } else {
              invokeVirtual(type, "compareTo", int.class, type);
            }
            mv.visitInsn(ICONST_0);
            jitPrimitiveOperation(operation, int.class);
          }
      }

      mv.visitLabel(shortcutEvaluation);
    }