Global Metrics

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

path: .metrics.cyclomatic.sum
old: 6.0
new: 39.0

path: .metrics.loc.ploc
old: 175.0
new: 184.0

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

path: .metrics.loc.sloc
old: 254.0
new: 260.0

path: .metrics.loc.blank
old: 10.0
new: 42.0

path: .metrics.loc.cloc
old: 69.0
new: 34.0

path: .metrics.nom.functions
old: 0.0
new: 8.0

path: .metrics.nom.total
old: 0.0
new: 8.0

path: .metrics.halstead.N1
old: 276.0
new: 468.0

path: .metrics.halstead.N2
old: 269.0
new: 325.0

path: .metrics.halstead.volume
old: 4185.316654230468
new: 5356.62578921563

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

path: .metrics.halstead.length
old: 545.0
new: 793.0

path: .metrics.halstead.difficulty
old: 8.362694300518134
new: 46.42857142857143

path: .metrics.halstead.bugs
old: 0.356666051511256
new: 1.3182461081405512

path: .metrics.halstead.estimated_program_length
old: 1508.3637582013937
new: 646.9937635307236

path: .metrics.halstead.effort
old: 35000.523730196765
new: 248700.4830707257

path: .metrics.halstead.n2
old: 193.0
new: 84.0

path: .metrics.halstead.vocabulary
old: 205.0
new: 108.0

path: .metrics.halstead.level
old: 0.11957868649318464
new: 0.021538461538461538

path: .metrics.halstead.purity_ratio
old: 2.767639923305309
new: 0.8158811646036868

path: .metrics.halstead.time
old: 1944.473540566487
new: 13816.693503929206

path: .metrics.nexits.average
old: null
new: 2.125

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

path: .metrics.mi.mi_visual_studio
old: 21.3746369150271
new: 15.964498231155764

path: .metrics.mi.mi_sei
old: 13.767658759752152
new: -5.776706398536774

path: .metrics.mi.mi_original
old: 36.55062912469634
new: 27.29929197527636

path: .metrics.nargs.average
old: null
new: 1.375

path: .metrics.nargs.sum
old: 0.0
new: 11.0

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

path: .metrics.cognitive.average
old: null
new: 4.625

Spaces Data

Minimal test - lines (16, 260)

path: .spaces[0].metrics.mi.mi_original
old: null
new: 28.66878586436843

path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 16.765371850507854

path: .spaces[0].metrics.mi.mi_sei
old: null
new: -5.4660060048984676

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

path: .spaces[0].metrics.nexits.average
old: null
new: 2.125

path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 177.0

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

path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 245.0

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

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

path: .spaces[0].metrics.cognitive.average
old: null
new: 4.625

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

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

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

path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 11.0

path: .spaces[0].metrics.nargs.average
old: null
new: 1.375

path: .spaces[0].metrics.halstead.level
old: null
new: 0.019778481012658226

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

path: .spaces[0].metrics.halstead.time
old: 0.0
new: 14543.099523637044

path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 5177.527520282175

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

path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 99.0

path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 1.364054531853209

path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 465.0

path: .spaces[0].metrics.halstead.length
old: 1.0
new: 781.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 577.2005018044988

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

path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 50.56

path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 0.739053139314339

path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 261775.79142546677

path: .spaces[0].metrics.nom.functions
old: 0.0
new: 8.0

path: .spaces[0].metrics.nom.total
old: 0.0
new: 8.0

Code

namespace mozilla {

namespace {

class AvailableEvent final : public Runnable {
 public:
  AvailableEvent(nsIInputStream* stream,
                 const std::function& aCallback)
      : Runnable("mozilla::AvailableEvent"),
        mStream(stream),
        mCallback(aCallback),
        mSize(-1) {
    mCallbackTarget = GetCurrentSerialEventTarget();
    MOZ_ASSERT(NS_IsMainThread());
  }

  NS_IMETHOD
  Run() override {
    // ping
    if (!NS_IsMainThread()) {
      uint64_t size = 0;
      if (NS_WARN_IF(NS_FAILED(mStream->Available(&size)))) {
        mSize = -1;
      } else {
        mSize = (int64_t)size;
      }

      mStream = nullptr;

      nsCOMPtr self(this);  // overly cute
      mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
      mCallbackTarget = nullptr;
      return NS_OK;
    }

    // pong
    std::function callback;
    callback.swap(mCallback);
    callback(mSize);
    return NS_OK;
  }

 private:
  nsCOMPtr mStream;
  std::function mCallback;
  nsCOMPtr mCallbackTarget;

  int64_t mSize;
};

}  // namespace

/* static */
bool InputStreamLengthHelper::GetSyncLength(nsIInputStream* aStream,
                                            int64_t* aLength) {
  MOZ_ASSERT(aStream);
  MOZ_ASSERT(aLength);

  *aLength = -1;

  // Sync length access.
  nsCOMPtr streamLength = do_QueryInterface(aStream);
  if (streamLength) {
    int64_t length = -1;
    nsresult rv = streamLength->Length(&length);

    // All good!
    if (NS_SUCCEEDED(rv)) {
      *aLength = length;
      return true;
    }

    // Already closed stream or an error occurred.
    if (rv == NS_BASE_STREAM_CLOSED ||
        NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
        NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
      return true;
    }
  }

  nsCOMPtr asyncStreamLength =
      do_QueryInterface(aStream);
  if (asyncStreamLength) {
    // GetAsyncLength should be used.
    return false;
  }

  // We cannot calculate the length of an async stream.
  nsCOMPtr asyncStream = do_QueryInterface(aStream);
  if (asyncStream) {
    return false;
  }

  // For main-thread only, we want to avoid calling ::Available() for blocking
  // streams.
  if (NS_IsMainThread()) {
    bool nonBlocking = false;
    if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) {
      // Let's return -1. There is nothing else we can do here.
      return true;
    }

    if (!nonBlocking) {
      return false;
    }
  }

  // Fallback using available().
  uint64_t available = 0;
  nsresult rv = aStream->Available(&available);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    // Let's return -1. There is nothing else we can do here.
    return true;
  }

  *aLength = (int64_t)available;
  return true;
}

/* static */
void InputStreamLengthHelper::GetAsyncLength(
    nsIInputStream* aStream,
    const std::function& aCallback) {
  MOZ_ASSERT(aStream);
  MOZ_ASSERT(aCallback);

  // We don't want to allow this class to be used on workers because we are not
  // using the correct Runnable types.
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() ||
                        !dom::IsCurrentThreadRunningWorker());

  RefPtr helper =
      new InputStreamLengthHelper(aStream, aCallback);

  // Let's be sure that we don't call ::Available() on main-thread.
  if (NS_IsMainThread()) {
    nsCOMPtr streamLength = do_QueryInterface(aStream);
    nsCOMPtr asyncStreamLength =
        do_QueryInterface(aStream);
    if (!streamLength && !asyncStreamLength) {
      // We cannot calculate the length of an async stream. We must fix the
      // caller if this happens.
#ifdef DEBUG
      nsCOMPtr asyncStream = do_QueryInterface(aStream);
      MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
#endif

      bool nonBlocking = false;
      if (NS_SUCCEEDED(aStream->IsNonBlocking(&nonBlocking)) && !nonBlocking) {
        nsCOMPtr target =
            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
        MOZ_ASSERT(target);

        RefPtr event = new AvailableEvent(aStream, aCallback);
        target->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
        return;
      }
    }
  }

  // Let's go async in order to have similar behaviors for sync and async
  // nsIInputStreamLength implementations.
  GetCurrentSerialEventTarget()->Dispatch(helper, NS_DISPATCH_NORMAL);
}

InputStreamLengthHelper::InputStreamLengthHelper(
    nsIInputStream* aStream,
    const std::function& aCallback)
    : Runnable("InputStreamLengthHelper"),
      mStream(aStream),
      mCallback(aCallback) {
  MOZ_ASSERT(aStream);
  MOZ_ASSERT(aCallback);
}

InputStreamLengthHelper::~InputStreamLengthHelper() = default;

NS_IMETHODIMP
InputStreamLengthHelper::Run() {
  // Sync length access.
  nsCOMPtr streamLength = do_QueryInterface(mStream);
  if (streamLength) {
    int64_t length = -1;
    nsresult rv = streamLength->Length(&length);

    // All good!
    if (NS_SUCCEEDED(rv)) {
      ExecCallback(length);
      return NS_OK;
    }

    // Already closed stream or an error occurred.
    if (rv == NS_BASE_STREAM_CLOSED ||
        NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
        NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
      ExecCallback(-1);
      return NS_OK;
    }
  }

  // Async length access.
  nsCOMPtr asyncStreamLength =
      do_QueryInterface(mStream);
  if (asyncStreamLength) {
    nsresult rv =
        asyncStreamLength->AsyncLengthWait(this, GetCurrentSerialEventTarget());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      ExecCallback(-1);
    }

    return NS_OK;
  }

  // Fallback using available().
  uint64_t available = 0;
  nsresult rv = mStream->Available(&available);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ExecCallback(-1);
    return NS_OK;
  }

  ExecCallback((int64_t)available);
  return NS_OK;
}

NS_IMETHODIMP
InputStreamLengthHelper::OnInputStreamLengthReady(
    nsIAsyncInputStreamLength* aStream, int64_t aLength) {
  ExecCallback(aLength);
  return NS_OK;
}

void InputStreamLengthHelper::ExecCallback(int64_t aLength) {
  MOZ_ASSERT(mCallback);

  std::function callback;
  callback.swap(mCallback);

  callback(aLength);
}

NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper, Runnable,
                            nsIInputStreamLengthCallback)

}  // namespace mozilla