Bug 716112 - Add an optional second argument to MOZ_ASSERT to allow explanations to accompany assertions, if desired. r=cjones
authorJeff Walden <jwalden@mit.edu>
Fri, 06 Jan 2012 15:51:27 -0600
changeset 84364 350305686094ca4ba0ccaf57c95279571c7223d3
parent 84363 88acaad9c766dcd306969e6bdf16b2cd81cec68e
child 84365 efdfbd08a4330368aef846bfc6f45d7275826bca
push id21842
push usermak77@bonardo.net
push dateFri, 13 Jan 2012 08:56:37 +0000
treeherdermozilla-central@8d4638feec54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs716112
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 716112 - Add an optional second argument to MOZ_ASSERT to allow explanations to accompany assertions, if desired. r=cjones
mfbt/Assertions.h
xpcom/ds/TimeStamp.h
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -147,31 +147,68 @@ extern "C" {
 extern MFBT_API(void)
 JS_Assert(const char* s, const char* file, int ln);
 
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
 /*
- * MOZ_ASSERT() is a "strong" assertion of state, like libc's
- * assert().
+ * MOZ_ASSERT(expr [, explanation-string]) asserts that |expr| must be truthy in
+ * debug builds.  If it is, execution continues.  Otherwise, an error message
+ * including the expression and the explanation-string (if provided) is printed,
+ * an attempt is made to invoke any existing debugger, and execution halts.
+ * MOZ_ASSERT is fatal: no recovery is possible.  Do not assert a condition
+ * which can correctly be falsy.
  *
- *   MOZ_ASSERT(twoToThePowerOf(5) == 32);
+ * The optional explanation-string, if provided, must be a string literal
+ * explaining the assertion.  It is intended for use with assertions whose
+ * correctness or rationale is non-obvious, and for assertions where the "real"
+ * condition being tested is best described prosaically.  Don't provide an
+ * explanation if it's not actually helpful.
  *
- * If a MOZ_ASSERT() fails in a debug build, the process in which it fails will
- * stop running in a loud and dramatic way.  It has no effect in an optimized
- * build.  This macro is designed to catch bugs during debugging, not "in the
- * field".
+ *   // No explanation needed: pointer arguments often must not be NULL.
+ *   MOZ_ASSERT(arg);
+ *
+ *   // An explanation can be helpful to explain exactly how we know an
+ *   // assertion is valid.
+ *   MOZ_ASSERT(state == WAITING_FOR_RESPONSE,
+ *              "given that <thingA> and <thingB>, we must have...");
+ *
+ *   // Or it might disambiguate multiple identical (save for their location)
+ *   // assertions of the same expression.
+ *   MOZ_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined(),
+ *              "we already set [[PrimitiveThis]] for this Boolean object");
+ *   MOZ_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined(),
+ *              "we already set [[PrimitiveThis]] for this String object");
+ *
+ * MOZ_ASSERT has no effect in non-debug builds.  It is designed to catch bugs
+ * *only* during debugging, not "in the field".
  */
 #ifdef DEBUG
-#  define MOZ_ASSERT(expr_)                                      \
-     ((expr_) ? ((void)0) : JS_Assert(#expr_, __FILE__, __LINE__))
+   /* First the single-argument form. */
+#  define MOZ_ASSERT_HELPER1(expr) \
+     ((expr) ? ((void)0) : JS_Assert(#expr, __FILE__, __LINE__))
+   /* Now the two-argument form. */
+#  define MOZ_ASSERT_HELPER2(expr, explain) \
+     ((expr) ? ((void)0) : JS_Assert(#expr " (" explain ")", __FILE__, __LINE__))
+   /* And now, helper macrology up the wazoo. */
+   /* Count the number of arguments passed to MOZ_ASSERT. */
+#  define MOZ_COUNT_ASSERT_ARGS(...) \
+     MOZ_COUNT_ASSERT_ARGS_IMPL(__VA_ARGS__, 2, 1, 0)
+#  define MOZ_COUNT_ASSERT_ARGS_IMPL(_1, _2, count, ...) \
+     count
+   /* Invoke the right helper. */
+#  define MOZ_ASSERT_VAHELP2(count, ...) MOZ_ASSERT_HELPER##count(__VA_ARGS__)
+#  define MOZ_ASSERT_VAHELP(count, ...) MOZ_ASSERT_VAHELP2(count, __VA_ARGS__)
+   /* The actual macro. */
+#  define MOZ_ASSERT(...) \
+     MOZ_ASSERT_VAHELP(MOZ_COUNT_ASSERT_ARGS(__VA_ARGS__), __VA_ARGS__)
 #else
-#  define MOZ_ASSERT(expr_) ((void)0)
+#  define MOZ_ASSERT(...) ((void)0)
 #endif /* DEBUG */
 
 /*
  * MOZ_ASSERT_IF(cond1, cond2) is equivalent to MOZ_ASSERT(cond2) if cond1 is
  * true.
  *
  *   MOZ_ASSERT_IF(isPrime(num), num == 2 || isOdd(num));
  *
--- a/xpcom/ds/TimeStamp.h
+++ b/xpcom/ds/TimeStamp.h
@@ -34,20 +34,21 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_TimeStamp_h
 #define mozilla_TimeStamp_h
 
+#include "mozilla/Assertions.h"
+
 #include "prinrval.h"
 #include "nsDebug.h"
 #include "prlong.h"
-#include "mozilla/Util.h"
 
 namespace mozilla {
 
 class TimeStamp;
 
 /**
  * Instances of this class represent the length of an interval of time.
  * Negative durations are allowed, meaning the end is before the start.
@@ -62,17 +63,17 @@ class TimeDuration
 {
 public:
   // The default duration is 0.
   TimeDuration() : mValue(0) {}
   // Allow construction using '0' as the initial value, for readability,
   // but no other numbers (so we don't have any implicit unit conversions).
   struct _SomethingVeryRandomHere;
   TimeDuration(_SomethingVeryRandomHere* aZero) : mValue(0) {
-    MOZ_ASSERT(!aZero && "Who's playing funny games here?");
+    MOZ_ASSERT(!aZero, "Who's playing funny games here?");
   }
   // Default copy-constructor and assignment are OK
 
   double ToSeconds() const;
   // Return a duration value that includes digits of time we think to
   // be significant.  This method should be used when displaying a
   // time to humans.
   double ToSecondsSigDigits() const;
@@ -211,82 +212,82 @@ public:
    * is monotonically increasing (i.e., does not decrease) over the
    * lifetime of this process' XPCOM session.
    */
   static TimeStamp Now();
   /**
    * Compute the difference between two timestamps. Both must be non-null.
    */
   TimeDuration operator-(const TimeStamp& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     PR_STATIC_ASSERT(-LL_MAXINT > LL_MININT);
     PRInt64 ticks = PRInt64(mValue - aOther.mValue);
     // Check for overflow.
     if (mValue > aOther.mValue) {
       if (ticks < 0) {
         ticks = LL_MAXINT;
       }
     } else {
       if (ticks > 0) {
         ticks = LL_MININT;
       }
     }
     return TimeDuration::FromTicks(ticks);
   }
 
   TimeStamp operator+(const TimeDuration& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
     return TimeStamp(mValue + aOther.mValue);
   }
   TimeStamp operator-(const TimeDuration& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
     return TimeStamp(mValue - aOther.mValue);
   }
   TimeStamp& operator+=(const TimeDuration& aOther) {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
     mValue += aOther.mValue;
     return *this;
   }
   TimeStamp& operator-=(const TimeDuration& aOther) {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
     mValue -= aOther.mValue;
     return *this;
   }
 
   bool operator<(const TimeStamp& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue < aOther.mValue;
   }
   bool operator<=(const TimeStamp& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue <= aOther.mValue;
   }
   bool operator>=(const TimeStamp& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue >= aOther.mValue;
   }
   bool operator>(const TimeStamp& aOther) const {
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue > aOther.mValue;
   }
   bool operator==(const TimeStamp& aOther) const {
     // Maybe it's ok to check == with null timestamps?
     MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue == aOther.mValue;
   }
   bool operator!=(const TimeStamp& aOther) const {
     // Maybe it's ok to check != with null timestamps?
-    MOZ_ASSERT(!IsNull() && "Cannot compute with a null value");
-    MOZ_ASSERT(!aOther.IsNull() && "Cannot compute with aOther null value");
+    MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+    MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue != aOther.mValue;
   }
 
   // Comparing TimeStamps for equality should be discouraged. Adding
   // two TimeStamps, or scaling TimeStamps, is nonsense and must never
   // be allowed.
 
   static NS_HIDDEN_(nsresult) Startup();