Global Metrics

path: .metrics.nexits.sum
old: 0.0
new: 29.0

path: .metrics.nexits.average
old: 0.0
new: 1.16

path: .metrics.cognitive.sum
old: 0.0
new: 33.0

path: .metrics.cognitive.average
old: 0.0
new: 1.32

path: .metrics.nom.functions
old: 1.0
new: 25.0

path: .metrics.nom.total
old: 1.0
new: 25.0

path: .metrics.halstead.time
old: 0.8333333333333334
new: 21622.116968298033

path: .metrics.halstead.n2
old: 1.0
new: 159.0

path: .metrics.halstead.purity_ratio
old: 0.9509775004326936
new: 0.9998330635565752

path: .metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 1272.7874899075202

path: .metrics.halstead.N1
old: 4.0
new: 734.0

path: .metrics.halstead.n1
old: 3.0
new: 24.0

path: .metrics.halstead.vocabulary
old: 4.0
new: 183.0

path: .metrics.halstead.level
old: 0.6666666666666666
new: 0.024582560296846013

path: .metrics.halstead.volume
old: 10.0
new: 9567.485894135589

path: .metrics.halstead.effort
old: 15.0
new: 389198.1054293646

path: .metrics.halstead.difficulty
old: 1.5
new: 40.679245283018865

path: .metrics.halstead.N2
old: 1.0
new: 539.0

path: .metrics.halstead.bugs
old: 0.002027400665191133
new: 1.776884600852695

path: .metrics.halstead.length
old: 5.0
new: 1273.0

path: .metrics.cyclomatic.average
old: 1.0
new: 2.0

path: .metrics.cyclomatic.sum
old: 2.0
new: 60.0

path: .metrics.loc.sloc
old: 8.0
new: 400.0

path: .metrics.loc.lloc
old: 0.0
new: 106.0

path: .metrics.loc.cloc
old: 4.0
new: 23.0

path: .metrics.loc.ploc
old: 2.0
new: 303.0

path: .metrics.loc.blank
old: 2.0
new: 74.0

path: .metrics.mi.mi_sei
old: 149.12257611294993
new: -33.444968236058116

path: .metrics.mi.mi_visual_studio
old: 73.02900850363604
new: 7.294982733057534

path: .metrics.mi.mi_original
old: 124.87960454121762
new: 12.474420473528385

path: .metrics.nargs.average
old: 1.0
new: 2.28

path: .metrics.nargs.sum
old: 1.0
new: 57.0

Spaces Data

Minimal test - lines (15, 400)

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.0344827586206895

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 59.0

path: .spaces[0].metrics.mi.mi_visual_studio
old: 86.29682642769582
new: 7.812382391283802

path: .spaces[0].metrics.mi.mi_sei
old: 137.29597390658574
new: -34.00352491869492

path: .spaces[0].metrics.mi.mi_original
old: 147.56757319135986
new: 13.359173889095302

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.32

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 33.0

path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 106.0

path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 18.0

path: .spaces[0].metrics.loc.sloc
old: 2.0
new: 386.0

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 73.0

path: .spaces[0].metrics.loc.ploc
old: 2.0
new: 295.0

path: .spaces[0].metrics.nargs.average
old: 1.0
new: 2.28

path: .spaces[0].metrics.nargs.sum
old: 1.0
new: 57.0

path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 151.0

path: .spaces[0].metrics.halstead.length
old: 5.0
new: 1265.0

path: .spaces[0].metrics.halstead.N1
old: 4.0
new: 734.0

path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 531.0

path: .spaces[0].metrics.halstead.volume
old: 10.0
new: 9425.782056467897

path: .spaces[0].metrics.halstead.n1
old: 3.0
new: 24.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 1203.0382156553944

path: .spaces[0].metrics.halstead.effort
old: 15.0
new: 397755.51830340025

path: .spaces[0].metrics.halstead.purity_ratio
old: 0.9509775004326936
new: 0.9510183522967544

path: .spaces[0].metrics.halstead.time
old: 0.8333333333333334
new: 22097.52879463335

path: .spaces[0].metrics.halstead.difficulty
old: 1.5
new: 42.19867549668874

path: .spaces[0].metrics.halstead.bugs
old: 0.002027400665191133
new: 1.802835997077912

path: .spaces[0].metrics.halstead.level
old: 0.6666666666666666
new: 0.02369742623979912

path: .spaces[0].metrics.halstead.vocabulary
old: 4.0
new: 175.0

path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.16

path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 29.0

path: .spaces[0].metrics.nom.functions
old: 1.0
new: 25.0

path: .spaces[0].metrics.nom.total
old: 1.0
new: 25.0

Code

namespace mozilla {

using namespace ipc;

class NonBlockingAsyncInputStream::AsyncWaitRunnable final
    : public CancelableRunnable {
  RefPtr mStream;
  nsCOMPtr mCallback;

 public:
  AsyncWaitRunnable(NonBlockingAsyncInputStream* aStream,
                    nsIInputStreamCallback* aCallback)
      : CancelableRunnable("AsyncWaitRunnable"),
        mStream(aStream),
        mCallback(aCallback) {}

  NS_IMETHOD
  Run() override {
    mStream->RunAsyncWaitCallback(this, mCallback.forget());
    return NS_OK;
  }

  nsresult Cancel() override {
    mStream = nullptr;
    return NS_OK;
  }
};

NS_IMPL_ADDREF(NonBlockingAsyncInputStream);
NS_IMPL_RELEASE(NonBlockingAsyncInputStream);

NonBlockingAsyncInputStream::WaitClosureOnly::WaitClosureOnly(
    AsyncWaitRunnable* aRunnable, nsIEventTarget* aEventTarget)
    : mRunnable(aRunnable), mEventTarget(aEventTarget) {}

NS_INTERFACE_MAP_BEGIN(NonBlockingAsyncInputStream)
  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                     mWeakCloneableInputStream)
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                     mWeakIPCSerializableInputStream)
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
                                     mWeakSeekableInputStream)
  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
                                     mWeakTellableInputStream)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_END

/* static */
nsresult NonBlockingAsyncInputStream::Create(
    already_AddRefed aInputStream,
    nsIAsyncInputStream** aResult) {
  MOZ_DIAGNOSTIC_ASSERT(aResult);

  nsCOMPtr inputStream = std::move(aInputStream);

  bool nonBlocking = false;
  nsresult rv = inputStream->IsNonBlocking(&nonBlocking);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  MOZ_DIAGNOSTIC_ASSERT(nonBlocking);

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  nsCOMPtr asyncInputStream =
      do_QueryInterface(inputStream);
  MOZ_DIAGNOSTIC_ASSERT(!asyncInputStream);
#endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED

  RefPtr stream =
      new NonBlockingAsyncInputStream(inputStream.forget());

  stream.forget(aResult);
  return NS_OK;
}

NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(
    already_AddRefed aInputStream)
    : mInputStream(std::move(aInputStream)),
      mWeakCloneableInputStream(nullptr),
      mWeakIPCSerializableInputStream(nullptr),
      mWeakSeekableInputStream(nullptr),
      mWeakTellableInputStream(nullptr),
      mLock("NonBlockingAsyncInputStream::mLock"),
      mClosed(false) {
  MOZ_ASSERT(mInputStream);

  nsCOMPtr cloneableStream =
      do_QueryInterface(mInputStream);
  if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
    mWeakCloneableInputStream = cloneableStream;
  }

  nsCOMPtr serializableStream =
      do_QueryInterface(mInputStream);
  if (serializableStream && SameCOMIdentity(mInputStream, serializableStream)) {
    mWeakIPCSerializableInputStream = serializableStream;
  }

  nsCOMPtr seekableStream = do_QueryInterface(mInputStream);
  if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
    mWeakSeekableInputStream = seekableStream;
  }

  nsCOMPtr tellableStream = do_QueryInterface(mInputStream);
  if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
    mWeakTellableInputStream = tellableStream;
  }
}

NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream() = default;

NS_IMETHODIMP
NonBlockingAsyncInputStream::Close() {
  RefPtr waitClosureOnlyRunnable;
  nsCOMPtr waitClosureOnlyEventTarget;

  {
    MutexAutoLock lock(mLock);

    if (mClosed) {
      // Here we could return NS_BASE_STREAM_CLOSED as well, but just to avoid
      // warning messages, let's make everybody happy with a NS_OK.
      return NS_OK;
    }

    mClosed = true;

    NS_ENSURE_STATE(mInputStream);
    nsresult rv = mInputStream->Close();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      mWaitClosureOnly.reset();
      return rv;
    }

    // If we have a WaitClosureOnly runnable, it's time to use it.
    if (mWaitClosureOnly.isSome()) {
      waitClosureOnlyRunnable = std::move(mWaitClosureOnly->mRunnable);
      waitClosureOnlyEventTarget = std::move(mWaitClosureOnly->mEventTarget);

      mWaitClosureOnly.reset();

      // Now we want to dispatch the asyncWaitCallback.
      mAsyncWaitCallback = waitClosureOnlyRunnable;
    }
  }

  if (waitClosureOnlyRunnable) {
    if (waitClosureOnlyEventTarget) {
      waitClosureOnlyEventTarget->Dispatch(waitClosureOnlyRunnable,
                                           NS_DISPATCH_NORMAL);
    } else {
      waitClosureOnlyRunnable->Run();
    }
  }

  return NS_OK;
}

// nsIInputStream interface

NS_IMETHODIMP
NonBlockingAsyncInputStream::Available(uint64_t* aLength) {
  nsresult rv = mInputStream->Available(aLength);
  // Don't issue warnings for legal condition NS_BASE_STREAM_CLOSED.
  if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  // Nothing more to read. Let's close the stream now.
  if (*aLength == 0) {
    mInputStream->Close();
    mClosed = true;
    return NS_BASE_STREAM_CLOSED;
  }

  return NS_OK;
}

NS_IMETHODIMP
NonBlockingAsyncInputStream::Read(char* aBuffer, uint32_t aCount,
                                  uint32_t* aReadCount) {
  return mInputStream->Read(aBuffer, aCount, aReadCount);
}

namespace {

class MOZ_RAII ReadSegmentsData {
 public:
  ReadSegmentsData(NonBlockingAsyncInputStream* aStream,
                   nsWriteSegmentFun aFunc, void* aClosure)
      : mStream(aStream), mFunc(aFunc), mClosure(aClosure) {}

  NonBlockingAsyncInputStream* mStream;
  nsWriteSegmentFun mFunc;
  void* mClosure;
};

nsresult ReadSegmentsWriter(nsIInputStream* aInStream, void* aClosure,
                            const char* aFromSegment, uint32_t aToOffset,
                            uint32_t aCount, uint32_t* aWriteCount) {
  ReadSegmentsData* data = static_cast(aClosure);
  return data->mFunc(data->mStream, data->mClosure, aFromSegment, aToOffset,
                     aCount, aWriteCount);
}

}  // namespace

NS_IMETHODIMP
NonBlockingAsyncInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                          void* aClosure, uint32_t aCount,
                                          uint32_t* aResult) {
  ReadSegmentsData data(this, aWriter, aClosure);
  return mInputStream->ReadSegments(ReadSegmentsWriter, &data, aCount, aResult);
}

NS_IMETHODIMP
NonBlockingAsyncInputStream::IsNonBlocking(bool* aNonBlocking) {
  *aNonBlocking = true;
  return NS_OK;
}

// nsICloneableInputStream interface

NS_IMETHODIMP
NonBlockingAsyncInputStream::GetCloneable(bool* aCloneable) {
  NS_ENSURE_STATE(mWeakCloneableInputStream);
  return mWeakCloneableInputStream->GetCloneable(aCloneable);
}

NS_IMETHODIMP
NonBlockingAsyncInputStream::Clone(nsIInputStream** aResult) {
  NS_ENSURE_STATE(mWeakCloneableInputStream);

  nsCOMPtr clonedStream;
  nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsCOMPtr asyncStream;
  rv = Create(clonedStream.forget(), getter_AddRefs(asyncStream));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  asyncStream.forget(aResult);
  return NS_OK;
}

// nsIAsyncInputStream interface

NS_IMETHODIMP
NonBlockingAsyncInputStream::CloseWithStatus(nsresult aStatus) {
  return Close();
}

NS_IMETHODIMP
NonBlockingAsyncInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                                       uint32_t aFlags,
                                       uint32_t aRequestedCount,
                                       nsIEventTarget* aEventTarget) {
  RefPtr runnable;
  {
    MutexAutoLock lock(mLock);

    if (aCallback && (mWaitClosureOnly.isSome() || mAsyncWaitCallback)) {
      return NS_ERROR_FAILURE;
    }

    if (!aCallback) {
      // Canceling previous callbacks.
      mWaitClosureOnly.reset();
      mAsyncWaitCallback = nullptr;
      return NS_OK;
    }

    // Maybe the stream is already closed.
    if (!mClosed) {
      uint64_t length;
      nsresult rv = mInputStream->Available(&length);
      if (NS_SUCCEEDED(rv) && length == 0) {
        mInputStream->Close();
        mClosed = true;
      }
    }

    runnable = new AsyncWaitRunnable(this, aCallback);
    if ((aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) && !mClosed) {
      mWaitClosureOnly.emplace(runnable, aEventTarget);
      return NS_OK;
    }

    mAsyncWaitCallback = runnable;
  }

  MOZ_ASSERT(runnable);

  if (aEventTarget) {
    return aEventTarget->Dispatch(runnable.forget());
  }

  return runnable->Run();
}

// nsIIPCSerializableInputStream

void NonBlockingAsyncInputStream::Serialize(
    mozilla::ipc::InputStreamParams& aParams,
    FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
    uint32_t aMaxSize, uint32_t* aSizeUsed,
    mozilla::ipc::ParentToChildStreamActorManager* aManager) {
  SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                    aSizeUsed, aManager);
}

void NonBlockingAsyncInputStream::Serialize(
    mozilla::ipc::InputStreamParams& aParams,
    FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
    uint32_t aMaxSize, uint32_t* aSizeUsed,
    mozilla::ipc::ChildToParentStreamActorManager* aManager) {
  SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                    aSizeUsed, aManager);
}

template 
void NonBlockingAsyncInputStream::SerializeInternal(
    mozilla::ipc::InputStreamParams& aParams,
    FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
    uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
  MOZ_ASSERT(mWeakIPCSerializableInputStream);
  InputStreamHelper::SerializeInputStream(mInputStream, aParams,
                                          aFileDescriptors, aDelayedStart,
                                          aMaxSize, aSizeUsed, aManager);
}

bool NonBlockingAsyncInputStream::Deserialize(
    const mozilla::ipc::InputStreamParams& aParams,
    const FileDescriptorArray& aFileDescriptors) {
  MOZ_CRASH("NonBlockingAsyncInputStream cannot be deserialized!");
  return true;
}

// nsISeekableStream

NS_IMETHODIMP
NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset) {
  NS_ENSURE_STATE(mWeakSeekableInputStream);
  return mWeakSeekableInputStream->Seek(aWhence, aOffset);
}

NS_IMETHODIMP
NonBlockingAsyncInputStream::SetEOF() {
  NS_ENSURE_STATE(mWeakSeekableInputStream);
  return NS_ERROR_NOT_IMPLEMENTED;
}

// nsITellableStream

NS_IMETHODIMP
NonBlockingAsyncInputStream::Tell(int64_t* aResult) {
  NS_ENSURE_STATE(mWeakTellableInputStream);
  return mWeakTellableInputStream->Tell(aResult);
}

void NonBlockingAsyncInputStream::RunAsyncWaitCallback(
    NonBlockingAsyncInputStream::AsyncWaitRunnable* aRunnable,
    already_AddRefed aCallback) {
  nsCOMPtr callback = std::move(aCallback);

  {
    MutexAutoLock lock(mLock);
    if (mAsyncWaitCallback != aRunnable) {
      // The callback has been canceled in the meantime.
      return;
    }

    mAsyncWaitCallback = nullptr;
  }

  callback->OnInputStreamReady(this);
}

}  // namespace mozilla