@Override
  public void checkInvariants_() {
    super.checkInvariants_();

    Object[] array = _array;

    if (array != null)
      for (Object object : array)
        if (object != null) Debug.assertion(!(object instanceof UserTObject));
  }
  private final void writeDebugInfo(int classId, int tag) {
    if (!Debug.COMMUNICATIONS || !getCheckCommunications()) throw new IllegalStateException();

    if (Debug.COMMUNICATIONS_LOG_ALL) {
      String name = ImmutableClass.ALL.get(classId).toString();
      Log.write(
          PlatformClass.getSimpleName(getClass())
              + ", "
              + ThreadAssert.getOrCreateCurrent().getWriterDebugCounter(this)
              + ", class: "
              + name
              + ", tag: "
              + tag);
    }

    Debug.assertion(classId == Helper.getInstance().getExpectedClass());
    long debugCounter = ThreadAssert.getOrCreateCurrent().getAndIncrementWriterDebugCounter(this);
    writeLongToBuffer(debugCounter);
    writeByteToBuffer((byte) classId);
    writeIntegerToBuffer(tag);
    writeIntegerToBuffer(getCustomDebugInfo1());
    writeIntegerToBuffer(getCustomDebugInfo2());
  }
  @Override
  public TObject.Version merge(TObject.Version target, TObject.Version next, int flags) {
    TListVersion source = (TListVersion) next;
    Object[] initialArray = _array;
    Object[] array = initialArray;

    if (source.getCleared()) {
      if (Debug.ENABLED) {
        Debug.assertion(source.getRemovals() == null);
        Debug.assertion(source.getRemovalsCount() == 0);
      }

      if (array != null) {
        if (array.length < source.size()) array = new Object[Utils.nextPowerOf2(source.size())];
        else for (int i = array.length - 1; i >= 0; i--) array[i] = null; // for GC
      }

      getReference().clearUserReferences();
      _size = 0;
    } else {
      if (array != null && array.length < source.size()) {
        Object[] temp = new Object[Utils.nextPowerOf2(source.size())];
        PlatformAdapter.arraycopy(array, 0, temp, 0, _size);
        array = temp;
      }
    }

    if (array == null) array = new Object[Utils.nextPowerOf2(source.size())];

    int copiedStart = Integer.MAX_VALUE;

    if (source.getCopied() != null) {
      copiedStart = source.getCopiedStart();
      int length = Math.min(source.getCopied().length, source.size() - copiedStart);

      if (Debug.ENABLED)
        for (int i = length; i < source.getCopied().length; i++)
          Debug.assertion(source.getCopied()[i] == null);

      for (int i = 0; i < length; i++) updateArray(array, copiedStart + i, source.getCopied()[i]);
    }

    if (source.getBits() != null) {
      for (int i = source.getBits().length - 1; i >= 0; i--) {
        if (source.getBits()[i] != null) {
          int firstWrite = source.getBits()[i].IntIndex << Bits.BITS_PER_UNIT_SHIFT;

          if (firstWrite < copiedStart) {
            for (int b = 0; b < Bits.BITS_PER_UNIT; b++) {
              if (Bits.get(source.getBits()[i].Value, b)) {
                int index = firstWrite + b;
                updateArray(array, index, source.get(index));
              }
            }
          }
        }
      }
    }

    if (array != initialArray) _array = array;

    if (Debug.ENABLED) Debug.assertion(_size + source.getSizeDelta() == source.size());

    for (int i = source.size(); i < _size; i++) updateArray(array, i, null);

    _size = source.size();

    return this;
  }