Global Metrics
path: .metrics.halstead.volume
old: 1124.7323510644935
new: 4963.955310300051
path: .metrics.halstead.N2
old: 66.0
new: 298.0
path: .metrics.halstead.difficulty
old: 16.076923076923077
new: 43.8235294117647
path: .metrics.halstead.purity_ratio
old: 1.4939651525575826
new: 0.9028615222897168
path: .metrics.halstead.time
old: 1004.568638343928
new: 12085.446752201104
path: .metrics.halstead.effort
old: 18082.235490190706
new: 217538.04153961985
path: .metrics.halstead.level
old: 0.06220095693779904
new: 0.022818791946308727
path: .metrics.halstead.n1
old: 19.0
new: 25.0
path: .metrics.halstead.n2
old: 39.0
new: 85.0
path: .metrics.halstead.N1
old: 126.0
new: 434.0
path: .metrics.halstead.bugs
old: 0.2296396233479145
new: 1.2056897111309235
path: .metrics.halstead.estimated_program_length
old: 286.84130929105584
new: 660.8946343160727
path: .metrics.halstead.length
old: 192.0
new: 732.0
path: .metrics.halstead.vocabulary
old: 58.0
new: 110.0
path: .metrics.mi.mi_sei
old: 43.85170420969874
new: 0.1975780724226368
path: .metrics.mi.mi_original
old: 65.04965547638089
new: 24.11929997220173
path: .metrics.mi.mi_visual_studio
old: 38.04073419671397
new: 14.104853784913292
path: .metrics.cognitive.sum
old: 2.0
new: 8.0
path: .metrics.cognitive.average
old: 0.25
new: 0.26666666666666666
path: .metrics.loc.blank
old: 8.0
new: 38.0
path: .metrics.loc.lloc
old: 14.0
new: 55.0
path: .metrics.loc.cloc
old: 7.0
new: 85.0
path: .metrics.loc.ploc
old: 48.0
new: 179.0
path: .metrics.loc.sloc
old: 63.0
new: 302.0
path: .metrics.cyclomatic.sum
old: 10.0
new: 44.0
path: .metrics.cyclomatic.average
old: 1.25
new: 1.2571428571428571
path: .metrics.nexits.average
old: 0.875
new: 0.4
path: .metrics.nexits.sum
old: 7.0
new: 12.0
path: .metrics.nom.functions
old: 6.0
new: 28.0
path: .metrics.nom.total
old: 8.0
new: 30.0
path: .metrics.nargs.average
old: 1.625
new: 0.5666666666666667
path: .metrics.nargs.sum
old: 13.0
new: 17.0
Spaces Data
Minimal test - lines (61, 300)
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.2647058823529411
path: .spaces[0].metrics.cyclomatic.sum
old: 5.0
new: 43.0
path: .spaces[0].metrics.mi.mi_original
old: 84.51884826167563
new: 28.23481217871415
path: .spaces[0].metrics.mi.mi_sei
old: 46.74317055377577
new: -1.0320186661940127
path: .spaces[0].metrics.mi.mi_visual_studio
old: 49.42622705361148
new: 16.51158606942348
path: .spaces[0].metrics.nargs.average
old: 3.0
new: 0.5666666666666667
path: .spaces[0].metrics.nargs.sum
old: 12.0
new: 17.0
path: .spaces[0].metrics.nom.total
old: 4.0
new: 30.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 2.0
path: .spaces[0].metrics.nom.functions
old: 4.0
new: 28.0
path: .spaces[0].metrics.halstead.length
old: 93.0
new: 721.0
path: .spaces[0].metrics.halstead.n2
old: 21.0
new: 77.0
path: .spaces[0].metrics.halstead.time
old: 196.21031746031747
new: 12539.020527428376
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.4009936955200455
new: 0.8302898313146678
path: .spaces[0].metrics.halstead.difficulty
old: 7.595238095238095
new: 46.91558441558441
path: .spaces[0].metrics.halstead.N1
old: 64.0
new: 432.0
path: .spaces[0].metrics.halstead.effort
old: 3531.785714285714
new: 225702.3694937108
path: .spaces[0].metrics.halstead.n1
old: 11.0
new: 25.0
path: .spaces[0].metrics.halstead.vocabulary
old: 32.0
new: 102.0
path: .spaces[0].metrics.halstead.volume
old: 465.0
new: 4810.818671561448
path: .spaces[0].metrics.halstead.bugs
old: 0.07730512895798068
new: 1.2356709144394495
path: .spaces[0].metrics.halstead.N2
old: 29.0
new: 289.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 130.29241368336423
new: 598.6389683778755
path: .spaces[0].metrics.halstead.level
old: 0.1316614420062696
new: 0.021314878892733567
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 0.26666666666666666
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 8.0
path: .spaces[0].metrics.loc.blank
old: 2.0
new: 34.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 40.0
path: .spaces[0].metrics.loc.lloc
old: 3.0
new: 55.0
path: .spaces[0].metrics.loc.ploc
old: 25.0
new: 166.0
path: .spaces[0].metrics.loc.sloc
old: 27.0
new: 240.0
path: .spaces[0].metrics.nexits.average
old: 0.75
new: 0.4
path: .spaces[0].metrics.nexits.sum
old: 3.0
new: 12.0
Code
namespace mozilla {
extern LazyLogModule gStateWatchingLog;
# define WATCH_LOG(x, ...) \
MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
/*
* AbstractWatcher is a superclass from which all watchers must inherit.
*/
class AbstractWatcher {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
AbstractWatcher() : mDestroyed(false) {}
bool IsDestroyed() { return mDestroyed; }
virtual void Notify() = 0;
protected:
virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
bool mDestroyed;
};
/*
* WatchTarget is a superclass from which all watchable things must inherit.
* Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
* needs only to invoke NotifyWatchers when something changes.
*
* The functionality that this class provides is not threadsafe, and should only
* be used on the thread that owns that WatchTarget.
*/
class WatchTarget {
public:
explicit WatchTarget(const char* aName) : mName(aName) {}
void AddWatcher(AbstractWatcher* aWatcher) {
MOZ_ASSERT(!mWatchers.Contains(aWatcher));
mWatchers.AppendElement(aWatcher);
}
void RemoveWatcher(AbstractWatcher* aWatcher) {
MOZ_ASSERT(mWatchers.Contains(aWatcher));
mWatchers.RemoveElement(aWatcher);
}
protected:
void NotifyWatchers() {
WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
PruneWatchers();
for (size_t i = 0; i < mWatchers.Length(); ++i) {
mWatchers[i]->Notify();
}
}
private:
// We don't have Watchers explicitly unregister themselves when they die,
// because then they'd need back-references to all the WatchTargets they're
// subscribed to, and WatchTargets aren't reference-counted. So instead we
// just prune dead ones at appropriate times, which works just fine.
void PruneWatchers() {
mWatchers.RemoveElementsBy(
[](const auto& watcher) { return watcher->IsDestroyed(); });
}
nsTArray> mWatchers;
protected:
const char* mName;
};
/*
* Watchable is a wrapper class that turns any primitive into a WatchTarget.
*/
template
class Watchable : public WatchTarget {
public:
Watchable(const T& aInitialValue, const char* aName)
: WatchTarget(aName), mValue(aInitialValue) {}
const T& Ref() const { return mValue; }
operator const T&() const { return Ref(); }
Watchable& operator=(const T& aNewValue) {
if (aNewValue != mValue) {
mValue = aNewValue;
NotifyWatchers();
}
return *this;
}
private:
Watchable(const Watchable& aOther) = delete;
Watchable& operator=(const Watchable& aOther) = delete;
T mValue;
};
// Manager class for state-watching. Declare one of these in any class for which
// you want to invoke method callbacks.
//
// Internally, WatchManager maintains one AbstractWatcher per callback method.
// Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple.
// This causes an AbstractWatcher for |Callback| to be instantiated if it
// doesn't already exist, and registers it with |WatchTarget|.
//
// Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire
// watch callbacks no more than once per task, once all other operations for
// that task have been completed.
//
// WatchManager is intended to be declared as a member of |OwnerType|
// objects. Given that, it and its owned objects can't hold permanent strong
// refs to the owner, since that would keep the owner alive indefinitely.
// Instead, it _only_ holds strong refs while waiting for Direct Tasks to fire.
// This ensures that everything is kept alive just long enough.
template
class WatchManager {
public:
typedef void (OwnerType::*CallbackMethod)();
explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
: mOwner(aOwner), mOwnerThread(aOwnerThread) {}
~WatchManager() {
if (!IsShutdown()) {
Shutdown();
}
}
bool IsShutdown() const { return !mOwner; }
// Shutdown needs to happen on mOwnerThread. If the WatchManager will be
// destroyed on a different thread, Shutdown() must be called manually.
void Shutdown() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
for (auto& watcher : mWatchers) {
watcher->Destroy();
}
mWatchers.Clear();
mOwner = nullptr;
}
void Watch(WatchTarget& aTarget, CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
aTarget.AddWatcher(&EnsureWatcher(aMethod));
}
void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
MOZ_ASSERT(watcher);
aTarget.RemoveWatcher(watcher);
}
void ManualNotify(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
MOZ_ASSERT(watcher);
watcher->Notify();
}
private:
class PerCallbackWatcher : public AbstractWatcher {
public:
PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread,
CallbackMethod aMethod)
: mOwner(aOwner),
mOwnerThread(aOwnerThread),
mCallbackMethod(aMethod) {}
void Destroy() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mDestroyed = true;
mOwner = nullptr;
}
void Notify() override {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(mOwner,
"mOwner is only null after destruction, "
"at which point we shouldn't be notified");
if (mNotificationPending) {
// We've already got a notification job in the pipe.
return;
}
mNotificationPending = true;
// Queue up our notification jobs to run in a stable state.
AbstractThread::DispatchDirectTask(
NS_NewRunnableFunction("WatchManager::PerCallbackWatcher::Notify",
[self = RefPtr(this),
owner = RefPtr(mOwner)]() {
if (!self->mDestroyed) {
((*owner).*(self->mCallbackMethod))();
}
self->mNotificationPending = false;
}));
}
bool CallbackMethodIs(CallbackMethod aMethod) const {
return mCallbackMethod == aMethod;
}
private:
~PerCallbackWatcher() = default;
OwnerType* mOwner; // Never null.
bool mNotificationPending = false;
RefPtr mOwnerThread;
CallbackMethod mCallbackMethod;
};
PerCallbackWatcher* GetWatcher(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
for (auto& watcher : mWatchers) {
if (watcher->CallbackMethodIs(aMethod)) {
return watcher;
}
}
return nullptr;
}
PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
if (watcher) {
return *watcher;
}
watcher = mWatchers
.AppendElement(MakeAndAddRef(
mOwner, mOwnerThread, aMethod))
->get();
return *watcher;
}
nsTArray> mWatchers;
OwnerType* mOwner;
RefPtr mOwnerThread;
};
# undef WATCH_LOG
} // namespace mozilla
Minimal test - lines (71, 81)
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 106.6746999031816
new: 104.97484534296882
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 78.30002840603022
new: 76.15311623999212
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 62.38286544045708
new: 61.388798446180594
path: .spaces[0].spaces[0].metrics.nexits.average
old: 1.0
new: 0.3333333333333333
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 9.0
new: 0.0
path: .spaces[0].spaces[0].metrics.nargs.average
old: 9.0
new: 0.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 10.0
new: 11.0
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.25
new: 0.14285714285714285
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 12.0
new: 14.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.026066162105227123
new: 0.03534764658819855
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 691.508495181978
new: 1092.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 67.01955000865388
new: 48.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 20.0
new: 16.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 12.0
new: 8.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 28.0
new: 25.0
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 172.8771237954945
new: 156.0
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 4.0
new: 7.0
path: .spaces[0].spaces[0].metrics.halstead.length
old: 40.0
new: 39.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.675488750216347
new: 1.2307692307692308
path: .spaces[0].spaces[0].metrics.halstead.time
old: 38.417138621221
new: 60.666666666666664
path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 3.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 3.0
Code
class AbstractWatcher {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
AbstractWatcher() : mDestroyed(false) {}
bool IsDestroyed() { return mDestroyed; }
virtual void Notify() = 0;
protected:
virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
bool mDestroyed;
};
Minimal test - lines (175, 296)
path: .spaces[0].spaces[3].metrics.nexits.sum
old: 1.0
new: 7.0
path: .spaces[0].spaces[3].metrics.nexits.average
old: 1.0
new: 0.4666666666666667
path: .spaces[0].spaces[3].metrics.nom.total
old: 1.0
new: 15.0
path: .spaces[0].spaces[3].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[3].metrics.nom.functions
old: 1.0
new: 14.0
path: .spaces[0].spaces[3].metrics.cyclomatic.average
old: 1.0
new: 1.4375
path: .spaces[0].spaces[3].metrics.cyclomatic.sum
old: 1.0
new: 23.0
path: .spaces[0].spaces[3].metrics.mi.mi_sei
old: 102.58917596407876
new: 9.3912101094776
path: .spaces[0].spaces[3].metrics.mi.mi_original
old: 123.51065405124744
new: 46.66596931013636
path: .spaces[0].spaces[3].metrics.mi.mi_visual_studio
old: 72.2284526615482
new: 27.290040532243484
path: .spaces[0].spaces[3].metrics.loc.ploc
old: 5.0
new: 102.0
path: .spaces[0].spaces[3].metrics.loc.lloc
old: 1.0
new: 37.0
path: .spaces[0].spaces[3].metrics.loc.blank
old: 0.0
new: 15.0
path: .spaces[0].spaces[3].metrics.loc.cloc
old: 0.0
new: 5.0
path: .spaces[0].spaces[3].metrics.loc.sloc
old: 5.0
new: 122.0
path: .spaces[0].spaces[3].metrics.cognitive.sum
old: 0.0
new: 6.0
path: .spaces[0].spaces[3].metrics.cognitive.average
old: 0.0
new: 0.4
path: .spaces[0].spaces[3].metrics.halstead.volume
old: 58.81033751683405
new: 2770.296325647598
path: .spaces[0].spaces[3].metrics.halstead.purity_ratio
old: 1.6265579090825428
new: 0.8469939979927766
path: .spaces[0].spaces[3].metrics.halstead.N2
old: 5.0
new: 186.0
path: .spaces[0].spaces[3].metrics.halstead.vocabulary
old: 11.0
new: 72.0
path: .spaces[0].spaces[3].metrics.halstead.difficulty
old: 4.375
new: 40.92
path: .spaces[0].spaces[3].metrics.halstead.length
old: 17.0
new: 449.0
path: .spaces[0].spaces[3].metrics.halstead.level
old: 0.22857142857142856
new: 0.02443792766373411
path: .spaces[0].spaces[3].metrics.halstead.n1
old: 7.0
new: 22.0
path: .spaces[0].spaces[3].metrics.halstead.N1
old: 12.0
new: 263.0
path: .spaces[0].spaces[3].metrics.halstead.bugs
old: 0.013484449818562038
new: 0.7807643576375038
path: .spaces[0].spaces[3].metrics.halstead.n2
old: 4.0
new: 50.0
path: .spaces[0].spaces[3].metrics.halstead.effort
old: 257.295226636149
new: 113360.52564549971
path: .spaces[0].spaces[3].metrics.halstead.estimated_program_length
old: 27.651484454403228
new: 380.3003050987567
path: .spaces[0].spaces[3].metrics.halstead.time
old: 14.294179257563831
new: 6297.8069803055405
path: .spaces[0].spaces[3].metrics.nargs.average
old: 0.0
new: 0.7333333333333333
path: .spaces[0].spaces[3].metrics.nargs.sum
old: 0.0
new: 11.0
Code
class WatchManager {
public:
typedef void (OwnerType::*CallbackMethod)();
explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
: mOwner(aOwner), mOwnerThread(aOwnerThread) {}
~WatchManager() {
if (!IsShutdown()) {
Shutdown();
}
}
bool IsShutdown() const { return !mOwner; }
// Shutdown needs to happen on mOwnerThread. If the WatchManager will be
// destroyed on a different thread, Shutdown() must be called manually.
void Shutdown() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
for (auto& watcher : mWatchers) {
watcher->Destroy();
}
mWatchers.Clear();
mOwner = nullptr;
}
void Watch(WatchTarget& aTarget, CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
aTarget.AddWatcher(&EnsureWatcher(aMethod));
}
void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
MOZ_ASSERT(watcher);
aTarget.RemoveWatcher(watcher);
}
void ManualNotify(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
MOZ_ASSERT(watcher);
watcher->Notify();
}
private:
class PerCallbackWatcher : public AbstractWatcher {
public:
PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread,
CallbackMethod aMethod)
: mOwner(aOwner),
mOwnerThread(aOwnerThread),
mCallbackMethod(aMethod) {}
void Destroy() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mDestroyed = true;
mOwner = nullptr;
}
void Notify() override {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(mOwner,
"mOwner is only null after destruction, "
"at which point we shouldn't be notified");
if (mNotificationPending) {
// We've already got a notification job in the pipe.
return;
}
mNotificationPending = true;
// Queue up our notification jobs to run in a stable state.
AbstractThread::DispatchDirectTask(
NS_NewRunnableFunction("WatchManager::PerCallbackWatcher::Notify",
[self = RefPtr(this),
owner = RefPtr(mOwner)]() {
if (!self->mDestroyed) {
((*owner).*(self->mCallbackMethod))();
}
self->mNotificationPending = false;
}));
}
bool CallbackMethodIs(CallbackMethod aMethod) const {
return mCallbackMethod == aMethod;
}
private:
~PerCallbackWatcher() = default;
OwnerType* mOwner; // Never null.
bool mNotificationPending = false;
RefPtr mOwnerThread;
CallbackMethod mCallbackMethod;
};
PerCallbackWatcher* GetWatcher(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
for (auto& watcher : mWatchers) {
if (watcher->CallbackMethodIs(aMethod)) {
return watcher;
}
}
return nullptr;
}
PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
PerCallbackWatcher* watcher = GetWatcher(aMethod);
if (watcher) {
return *watcher;
}
watcher = mWatchers
.AppendElement(MakeAndAddRef(
mOwner, mOwnerThread, aMethod))
->get();
return *watcher;
}
nsTArray> mWatchers;
OwnerType* mOwner;
RefPtr mOwnerThread;
};
Minimal test - lines (134, 155)
path: .spaces[0].spaces[2].metrics.nom.functions
old: 1.0
new: 6.0
path: .spaces[0].spaces[2].metrics.nom.total
old: 1.0
new: 6.0
path: .spaces[0].spaces[2].metrics.cognitive.average
old: 0.0
new: 0.16666666666666666
path: .spaces[0].spaces[2].metrics.cognitive.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[2].metrics.cyclomatic.average
old: 1.0
new: 1.1428571428571428
path: .spaces[0].spaces[2].metrics.cyclomatic.sum
old: 1.0
new: 8.0
path: .spaces[0].spaces[2].metrics.halstead.purity_ratio
old: 1.8823529411764703
new: 0.97981924439407
path: .spaces[0].spaces[2].metrics.halstead.n2
old: 4.0
new: 10.0
path: .spaces[0].spaces[2].metrics.halstead.effort
old: 304.72181256129824
new: 7565.43119340755
path: .spaces[0].spaces[2].metrics.halstead.bugs
old: 0.015094350855465146
new: 0.1284599856398078
path: .spaces[0].spaces[2].metrics.halstead.volume
old: 60.94436251225965
new: 375.45564235273207
path: .spaces[0].spaces[2].metrics.halstead.n1
old: 8.0
new: 13.0
path: .spaces[0].spaces[2].metrics.halstead.N1
old: 12.0
new: 52.0
path: .spaces[0].spaces[2].metrics.halstead.N2
old: 5.0
new: 31.0
path: .spaces[0].spaces[2].metrics.halstead.estimated_program_length
old: 32.0
new: 81.32499728470782
path: .spaces[0].spaces[2].metrics.halstead.length
old: 17.0
new: 83.0
path: .spaces[0].spaces[2].metrics.halstead.level
old: 0.2
new: 0.04962779156327544
path: .spaces[0].spaces[2].metrics.halstead.difficulty
old: 5.0
new: 20.15
path: .spaces[0].spaces[2].metrics.halstead.time
old: 16.92898958673879
new: 420.30173296708614
path: .spaces[0].spaces[2].metrics.halstead.vocabulary
old: 12.0
new: 23.0
path: .spaces[0].spaces[2].metrics.loc.blank
old: 0.0
new: 4.0
path: .spaces[0].spaces[2].metrics.loc.sloc
old: 4.0
new: 22.0
path: .spaces[0].spaces[2].metrics.loc.ploc
old: 4.0
new: 18.0
path: .spaces[0].spaces[2].metrics.loc.lloc
old: 1.0
new: 6.0
path: .spaces[0].spaces[2].metrics.nargs.average
old: 0.0
new: 0.5
path: .spaces[0].spaces[2].metrics.nargs.sum
old: 0.0
new: 3.0
path: .spaces[0].spaces[2].metrics.nexits.average
old: 1.0
new: 0.5
path: .spaces[0].spaces[2].metrics.nexits.sum
old: 1.0
new: 3.0
path: .spaces[0].spaces[2].metrics.mi.mi_sei
old: 107.53701147893022
new: 52.444214732937326
path: .spaces[0].spaces[2].metrics.mi.mi_visual_studio
old: 74.23405396973165
new: 51.61332310835995
path: .spaces[0].spaces[2].metrics.mi.mi_original
old: 126.9402322882411
new: 88.25878251529551
Code
class Watchable : public WatchTarget {
public:
Watchable(const T& aInitialValue, const char* aName)
: WatchTarget(aName), mValue(aInitialValue) {}
const T& Ref() const { return mValue; }
operator const T&() const { return Ref(); }
Watchable& operator=(const T& aNewValue) {
if (aNewValue != mValue) {
mValue = aNewValue;
NotifyWatchers();
}
return *this;
}
private:
Watchable(const Watchable& aOther) = delete;
Watchable& operator=(const Watchable& aOther) = delete;
T mValue;
};
Minimal test - lines (91, 128)
path: .spaces[0].spaces[1].metrics.nom.total
old: 1.0
new: 6.0
path: .spaces[0].spaces[1].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[1].metrics.nom.functions
old: 1.0
new: 5.0
path: .spaces[0].spaces[1].metrics.nargs.average
old: 3.0
new: 0.5
path: .spaces[0].spaces[1].metrics.nexits.average
old: 0.0
new: 0.16666666666666666
path: .spaces[0].spaces[1].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[1].metrics.cognitive.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[1].metrics.cognitive.average
old: 0.0
new: 0.16666666666666666
path: .spaces[0].spaces[1].metrics.cyclomatic.sum
old: 1.0
new: 7.0
path: .spaces[0].spaces[1].metrics.cyclomatic.average
old: 1.0
new: 1.1666666666666667
path: .spaces[0].spaces[1].metrics.halstead.difficulty
old: 3.0
new: 18.0
path: .spaces[0].spaces[1].metrics.halstead.N1
old: 10.0
new: 80.0
path: .spaces[0].spaces[1].metrics.halstead.level
old: 0.3333333333333333
new: 0.05555555555555555
path: .spaces[0].spaces[1].metrics.halstead.purity_ratio
old: 1.8079610319175832
new: 1.4704234982333058
path: .spaces[0].spaces[1].metrics.halstead.time
old: 8.648579046593243
new: 705.4144181112728
path: .spaces[0].spaces[1].metrics.halstead.estimated_program_length
old: 27.11941547876375
new: 191.15505477032977
path: .spaces[0].spaces[1].metrics.halstead.n2
old: 5.0
new: 25.0
path: .spaces[0].spaces[1].metrics.halstead.volume
old: 51.89147427955946
new: 705.4144181112728
path: .spaces[0].spaces[1].metrics.halstead.bugs
old: 0.009646214643915437
new: 0.18142200824967217
path: .spaces[0].spaces[1].metrics.halstead.N2
old: 5.0
new: 50.0
path: .spaces[0].spaces[1].metrics.halstead.n1
old: 6.0
new: 18.0
path: .spaces[0].spaces[1].metrics.halstead.effort
old: 155.67442283867837
new: 12697.45952600291
path: .spaces[0].spaces[1].metrics.halstead.vocabulary
old: 11.0
new: 43.0
path: .spaces[0].spaces[1].metrics.halstead.length
old: 15.0
new: 130.0
path: .spaces[0].spaces[1].metrics.mi.mi_sei
old: 108.74338677859146
new: 59.25583590014251
path: .spaces[0].spaces[1].metrics.mi.mi_original
old: 127.77642792589845
new: 76.35541983490009
path: .spaces[0].spaces[1].metrics.mi.mi_visual_studio
old: 74.72305726660727
new: 44.65229230111117
path: .spaces[0].spaces[1].metrics.loc.lloc
old: 0.0
new: 10.0
path: .spaces[0].spaces[1].metrics.loc.ploc
old: 4.0
new: 28.0
path: .spaces[0].spaces[1].metrics.loc.blank
old: 0.0
new: 6.0
path: .spaces[0].spaces[1].metrics.loc.cloc
old: 0.0
new: 4.0
path: .spaces[0].spaces[1].metrics.loc.sloc
old: 4.0
new: 38.0
Code
class WatchTarget {
public:
explicit WatchTarget(const char* aName) : mName(aName) {}
void AddWatcher(AbstractWatcher* aWatcher) {
MOZ_ASSERT(!mWatchers.Contains(aWatcher));
mWatchers.AppendElement(aWatcher);
}
void RemoveWatcher(AbstractWatcher* aWatcher) {
MOZ_ASSERT(mWatchers.Contains(aWatcher));
mWatchers.RemoveElement(aWatcher);
}
protected:
void NotifyWatchers() {
WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
PruneWatchers();
for (size_t i = 0; i < mWatchers.Length(); ++i) {
mWatchers[i]->Notify();
}
}
private:
// We don't have Watchers explicitly unregister themselves when they die,
// because then they'd need back-references to all the WatchTargets they're
// subscribed to, and WatchTargets aren't reference-counted. So instead we
// just prune dead ones at appropriate times, which works just fine.
void PruneWatchers() {
mWatchers.RemoveElementsBy(
[](const auto& watcher) { return watcher->IsDestroyed(); });
}
nsTArray> mWatchers;
protected:
const char* mName;
};