@NotNull
  @Override
  public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) {
    if (v.equals(w)) return v;

    if (tolerantToUninitializedValues) {
      if (v == StrictBasicValue.UNINITIALIZED_VALUE) return w;
      if (w == StrictBasicValue.UNINITIALIZED_VALUE) return v;
    } else if (v == StrictBasicValue.UNINITIALIZED_VALUE
        || w == StrictBasicValue.UNINITIALIZED_VALUE) {
      return StrictBasicValue.UNINITIALIZED_VALUE;
    }

    // if merge of two references then `lub` is java/lang/Object
    // arrays also are BasicValues with reference type's
    if (isReference(v) && isReference(w)) {
      return StrictBasicValue.REFERENCE_VALUE;
    }

    // if merge of something can be stored in int var (int, char, boolean, byte, character)
    if (v.getType().getOpcode(Opcodes.ISTORE) == Opcodes.ISTORE
        && w.getType().getOpcode(Opcodes.ISTORE) == Opcodes.ISTORE) {
      return StrictBasicValue.INT_VALUE;
    }

    return StrictBasicValue.UNINITIALIZED_VALUE;
  }
 private static boolean isReference(@NotNull BasicValue v) {
   return v.getType().getSort() == Type.OBJECT || v.getType().getSort() == Type.ARRAY;
 }
  @Override
  public BasicValue binaryOperation(
      @NotNull AbstractInsnNode insn, @NotNull BasicValue value1, @NotNull BasicValue value2)
      throws AnalyzerException {
    if (insn.getOpcode() == Opcodes.AALOAD) {
      Type arrayType = value1.getType();
      if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
        return new StrictBasicValue(arrayType.getElementType());
      }
    }

    switch (insn.getOpcode()) {
      case IALOAD:
      case BALOAD:
      case CALOAD:
      case SALOAD:
      case IADD:
      case ISUB:
      case IMUL:
      case IDIV:
      case IREM:
      case ISHL:
      case ISHR:
      case IUSHR:
      case IAND:
      case IOR:
      case IXOR:
        return StrictBasicValue.INT_VALUE;
      case FALOAD:
      case FADD:
      case FSUB:
      case FMUL:
      case FDIV:
      case FREM:
        return StrictBasicValue.FLOAT_VALUE;
      case LALOAD:
      case LADD:
      case LSUB:
      case LMUL:
      case LDIV:
      case LREM:
      case LSHL:
      case LSHR:
      case LUSHR:
      case LAND:
      case LOR:
      case LXOR:
        return StrictBasicValue.LONG_VALUE;
      case DALOAD:
      case DADD:
      case DSUB:
      case DMUL:
      case DDIV:
      case DREM:
        return StrictBasicValue.DOUBLE_VALUE;
      case AALOAD:
        return StrictBasicValue.REFERENCE_VALUE;
      case LCMP:
      case FCMPL:
      case FCMPG:
      case DCMPL:
      case DCMPG:
        return StrictBasicValue.INT_VALUE;
      case IF_ICMPEQ:
      case IF_ICMPNE:
      case IF_ICMPLT:
      case IF_ICMPGE:
      case IF_ICMPGT:
      case IF_ICMPLE:
      case IF_ACMPEQ:
      case IF_ACMPNE:
      case PUTFIELD:
        return null;
      default:
        throw new Error("Internal error.");
    }
  }