Global Metrics

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

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

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

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

path: .metrics.loc.blank
old: 19.0
new: 55.0

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

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

path: .metrics.loc.ploc
old: 83.0
new: 253.0

path: .metrics.loc.sloc
old: 108.0
new: 342.0

path: .metrics.mi.mi_original
old: 54.1579465950381
new: 17.276604743750113

path: .metrics.mi.mi_sei
old: 20.89786254430325
new: -21.61059094316863

path: .metrics.mi.mi_visual_studio
old: 31.671313798267896
new: 10.103277627924042

path: .metrics.nargs.average
old: 8.0
new: 0.5384615384615384

path: .metrics.nargs.sum
old: 8.0
new: 14.0

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

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

path: .metrics.halstead.n1
old: 13.0
new: 26.0

path: .metrics.halstead.n2
old: 80.0
new: 134.0

path: .metrics.halstead.vocabulary
old: 93.0
new: 160.0

path: .metrics.halstead.level
old: 0.08606777837547068
new: 0.025388404698749527

path: .metrics.halstead.bugs
old: 0.2744773629700805
new: 1.4637476836917094

path: .metrics.halstead.time
old: 1312.711155376145
new: 16166.22832221345

path: .metrics.halstead.difficulty
old: 11.61875
new: 39.38805970149254

path: .metrics.halstead.estimated_program_length
old: 553.8599639268232
new: 1069.06738419301

path: .metrics.halstead.purity_ratio
old: 1.780900205552486
new: 1.0595315997948562

path: .metrics.halstead.N2
old: 143.0
new: 406.0

path: .metrics.halstead.effort
old: 23628.800796770607
new: 290992.1097998421

path: .metrics.halstead.volume
old: 2033.6783902545976
new: 7387.825447741349

path: .metrics.halstead.N1
old: 168.0
new: 603.0

path: .metrics.halstead.length
old: 311.0
new: 1009.0

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

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

Spaces Data

Minimal test - lines (25, 259)

path: .spaces[0].metrics.loc.cloc
old: 1.0
new: 22.0

path: .spaces[0].metrics.loc.ploc
old: 72.0
new: 174.0

path: .spaces[0].metrics.loc.blank
old: 16.0
new: 39.0

path: .spaces[0].metrics.loc.sloc
old: 89.0
new: 235.0

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

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

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

path: .spaces[0].metrics.mi.mi_sei
old: 16.251607584085797
new: -7.3885781428147475

path: .spaces[0].metrics.mi.mi_original
old: 57.71802216163934
new: 28.637365831306624

path: .spaces[0].metrics.mi.mi_visual_studio
old: 33.75322933429201
new: 16.746997562167618

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

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

path: .spaces[0].metrics.halstead.N2
old: 136.0
new: 293.0

path: .spaces[0].metrics.halstead.length
old: 304.0
new: 742.0

path: .spaces[0].metrics.halstead.N1
old: 168.0
new: 449.0

path: .spaces[0].metrics.halstead.level
old: 0.08371040723981901
new: 0.02677868206878446

path: .spaces[0].metrics.halstead.time
old: 1299.8880355108136
new: 10775.5697167756

path: .spaces[0].metrics.halstead.volume
old: 1958.6548227380133
new: 5194.0

path: .spaces[0].metrics.halstead.difficulty
old: 11.945945945945946
new: 37.34313725490196

path: .spaces[0].metrics.halstead.n1
old: 13.0
new: 26.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: 507.6052653923765
new: 802.7988175527609

path: .spaces[0].metrics.halstead.effort
old: 23397.984639194645
new: 193960.2549019608

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.6697541624749228
new: 1.0819391072139637

path: .spaces[0].metrics.halstead.bugs
old: 0.2726869658563649
new: 1.1169161639642688

path: .spaces[0].metrics.halstead.vocabulary
old: 87.0
new: 128.0

path: .spaces[0].metrics.halstead.n2
old: 74.0
new: 102.0

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

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

path: .spaces[0].metrics.cyclomatic.sum
old: 5.0
new: 41.0

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

path: .spaces[0].metrics.nargs.average
old: 8.0
new: 0.6666666666666666

path: .spaces[0].metrics.nargs.sum
old: 8.0
new: 14.0

Code

namespace {

class AndroidUiThread;
class AndroidUiTask;

StaticAutoPtr > sTaskQueue;
StaticAutoPtr sTaskQueueLock;
StaticRefPtr sThread;
static bool sThreadDestroyed;
static MessageLoop* sMessageLoop;
static Atomic sMessageLoopAccessMonitor;

void EnqueueTask(already_AddRefed aTask, int aDelayMs);

/*
 * The AndroidUiThread is derived from nsThread so that nsIRunnable objects that
 * get dispatched may be intercepted. Only nsIRunnable objects that need to be
 * synchronously executed are passed into the nsThread to be queued. All other
 * nsIRunnable object are immediately dispatched to the Android UI thread.
 * AndroidUiThread is derived from nsThread instead of being an nsIEventTarget
 * wrapper that contains an nsThread object because if nsIRunnable objects with
 * a delay were dispatch directly to an nsThread object, such as obtained from
 * nsThreadManager::GetCurrentThread(), the nsIRunnable could get stuck in the
 * nsThread nsIRunnable queue. This is due to the fact that Android controls the
 * event loop in the Android UI thread and has no knowledge of when the nsThread
 * needs to be drained.
 */

class AndroidUiThread : public nsThread {
 public:
  NS_INLINE_DECL_REFCOUNTING_INHERITED(AndroidUiThread, nsThread)
  AndroidUiThread()
      : nsThread(
            MakeNotNull(MakeUnique()),
            nsThread::NOT_MAIN_THREAD, 0) {}

  nsresult Dispatch(already_AddRefed aEvent,
                    uint32_t aFlags) override;
  nsresult DelayedDispatch(already_AddRefed aEvent,
                           uint32_t aDelayMs) override;

 private:
  ~AndroidUiThread() {}
};

NS_IMETHODIMP
AndroidUiThread::Dispatch(already_AddRefed aEvent,
                          uint32_t aFlags) {
  if (aFlags & NS_DISPATCH_SYNC) {
    return nsThread::Dispatch(std::move(aEvent), aFlags);
  } else {
    EnqueueTask(std::move(aEvent), 0);
    return NS_OK;
  }
}

NS_IMETHODIMP
AndroidUiThread::DelayedDispatch(already_AddRefed aEvent,
                                 uint32_t aDelayMs) {
  EnqueueTask(std::move(aEvent), aDelayMs);
  return NS_OK;
}

static void PumpEvents() { NS_ProcessPendingEvents(sThread.get()); }

class ThreadObserver : public nsIThreadObserver {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITHREADOBSERVER

  ThreadObserver() {}

 private:
  virtual ~ThreadObserver() {}
};

NS_IMPL_ISUPPORTS(ThreadObserver, nsIThreadObserver)

NS_IMETHODIMP
ThreadObserver::OnDispatchedEvent() {
  EnqueueTask(NS_NewRunnableFunction("PumpEvents", &PumpEvents), 0);
  return NS_OK;
}

NS_IMETHODIMP
ThreadObserver::OnProcessNextEvent(nsIThreadInternal* thread, bool mayWait) {
  return NS_OK;
}

NS_IMETHODIMP
ThreadObserver::AfterProcessNextEvent(nsIThreadInternal* thread,
                                      bool eventWasProcessed) {
  return NS_OK;
}

class AndroidUiTask : public LinkedListElement {
  using TimeStamp = mozilla::TimeStamp;
  using TimeDuration = mozilla::TimeDuration;

 public:
  explicit AndroidUiTask(already_AddRefed aTask)
      : mTask(aTask),
        mRunTime()  // Null timestamp representing no delay.
  {}

  AndroidUiTask(already_AddRefed aTask, int aDelayMs)
      : mTask(aTask),
        mRunTime(TimeStamp::Now() + TimeDuration::FromMilliseconds(aDelayMs)) {}

  bool IsEarlierThan(const AndroidUiTask& aOther) const {
    if (mRunTime) {
      return aOther.mRunTime ? mRunTime < aOther.mRunTime : false;
    }
    // In the case of no delay, we're earlier if aOther has a delay.
    // Otherwise, we're not earlier, to maintain task order.
    return !!aOther.mRunTime;
  }

  int64_t MillisecondsToRunTime() const {
    if (mRunTime) {
      return int64_t((mRunTime - TimeStamp::Now()).ToMilliseconds());
    }
    return 0;
  }

  already_AddRefed TakeTask() { return mTask.forget(); }

 private:
  nsCOMPtr mTask;
  const TimeStamp mRunTime;
};

class CreateOnUiThread : public Runnable {
 public:
  CreateOnUiThread() : Runnable("CreateOnUiThread") {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(!sThreadDestroyed);
    MOZ_ASSERT(sMessageLoopAccessMonitor);
    MonitorAutoLock lock(*sMessageLoopAccessMonitor);
    sThread = new AndroidUiThread();
    sThread->InitCurrentThread();
    sThread->SetObserver(new ThreadObserver());
    PROFILER_REGISTER_THREAD("AndroidUI");
    sMessageLoop =
        new MessageLoop(MessageLoop::TYPE_MOZILLA_ANDROID_UI, sThread.get());
    lock.NotifyAll();
    return NS_OK;
  }
};

class DestroyOnUiThread : public Runnable {
 public:
  DestroyOnUiThread() : Runnable("DestroyOnUiThread"), mDestroyed(false) {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(!sThreadDestroyed);
    MOZ_ASSERT(sMessageLoopAccessMonitor);
    MOZ_ASSERT(sTaskQueue);
    MonitorAutoLock lock(*sMessageLoopAccessMonitor);
    sThreadDestroyed = true;

    {
      // Flush the queue
      MutexAutoLock lock(*sTaskQueueLock);
      while (AndroidUiTask* task = sTaskQueue->getFirst()) {
        delete task;
      }
    }

    delete sMessageLoop;
    sMessageLoop = nullptr;
    MOZ_ASSERT(sThread);
    PROFILER_UNREGISTER_THREAD();
    nsThreadManager::get().UnregisterCurrentThread(*sThread);
    sThread = nullptr;
    mDestroyed = true;
    lock.NotifyAll();
    return NS_OK;
  }

  void WaitForDestruction() {
    MOZ_ASSERT(sMessageLoopAccessMonitor);
    MonitorAutoLock lock(*sMessageLoopAccessMonitor);
    while (!mDestroyed) {
      lock.Wait();
    }
  }

 private:
  bool mDestroyed;
};

void EnqueueTask(already_AddRefed aTask, int aDelayMs) {
  if (sThreadDestroyed) {
    return;
  }

  // add the new task into the sTaskQueue, sorted with
  // the earliest task first in the queue
  AndroidUiTask* newTask =
      (aDelayMs ? new AndroidUiTask(std::move(aTask), aDelayMs)
                : new AndroidUiTask(std::move(aTask)));

  bool headOfList = false;
  {
    MOZ_ASSERT(sTaskQueue);
    MOZ_ASSERT(sTaskQueueLock);
    MutexAutoLock lock(*sTaskQueueLock);

    AndroidUiTask* task = sTaskQueue->getFirst();

    while (task) {
      if (newTask->IsEarlierThan(*task)) {
        task->setPrevious(newTask);
        break;
      }
      task = task->getNext();
    }

    if (!newTask->isInList()) {
      sTaskQueue->insertBack(newTask);
    }
    headOfList = !newTask->getPrevious();
  }

  if (headOfList) {
    // if we're inserting it at the head of the queue, notify Java because
    // we need to get a callback at an earlier time than the last scheduled
    // callback
    java::GeckoThread::RequestUiThreadCallback(int64_t(aDelayMs));
  }
}

}  // namespace

Minimal test - lines (27, 27)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Code

class AndroidUiThread;