Bug 1463462 - Make gray marking assertions call a JSAPI function r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 06 Dec 2018 16:28:10 -0500
changeset 450671 1544326ba29a387f1240415af38da7a33f5083ef
parent 450670 e3fc6ddd9a5316edac3737f92666e3cd0c08a44e
child 450672 42f073dedf5fd708e118833b4ddf63a19907485a
push id110516
push userjcoppeard@mozilla.com
push dateFri, 14 Dec 2018 14:22:31 +0000
treeherdermozilla-inbound@fd4d12eb1b97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1463462
milestone66.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 1463462 - Make gray marking assertions call a JSAPI function r=sfink
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/CallbackObject.h
dom/bindings/Codegen.py
js/public/CallArgs.h
js/public/HeapAPI.h
js/public/Id.h
js/public/Proxy.h
js/public/RootingAPI.h
js/public/Value.h
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/Cell.h
js/src/gc/GC.cpp
js/src/jsapi-tests/testGCGrayMarking.cpp
js/src/jsapi.cpp
js/src/proxy/Wrapper.cpp
js/src/vm/Compartment-inl.h
js/src/vm/Compartment.cpp
js/src/vm/Debugger.h
js/src/vm/JSContext-inl.h
js/src/vm/TaggedProto.h
js/xpconnect/wrappers/WrapperFactory.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3953,17 +3953,17 @@ JS::Handle<JSObject*> GetPerInterfaceObj
    * changed after they have been set.
    *
    * Calling address() avoids the read barrier that does gray unmarking, but
    * it's not possible for the object to be gray here.
    */
 
   const JS::Heap<JSObject*>& entrySlot =
       protoAndIfaceCache.EntrySlotMustExist(aSlotId);
-  MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot));
+  JS::AssertObjectIsNotGray(entrySlot);
   return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
 }
 
 namespace binding_detail {
 bool IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
                      JSJitGetterOp aGetter,
                      const Prefable<const JSPropertySpec>* aAttributes) {
   MOZ_ASSERT(aAttributes);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1444,23 +1444,23 @@ template <typename T, bool hasWrapObject
 struct WrapNativeHelper {
   static inline JSObject* Wrap(JSContext* cx, T* parent,
                                nsWrapperCache* cache) {
     MOZ_ASSERT(cache);
 
     JSObject* obj;
     if ((obj = cache->GetWrapper())) {
       // GetWrapper always unmarks gray.
-      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+      JS::AssertObjectIsNotGray(obj);
       return obj;
     }
 
     // WrapObject never returns a gray thing.
     obj = parent->WrapObject(cx, nullptr);
-    MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+    JS::AssertObjectIsNotGray(obj);
 
     return obj;
   }
 };
 
 // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
 // this case it must be nsISupports.
 template <typename T>
@@ -1470,22 +1470,22 @@ struct WrapNativeHelper<T, false> {
     JSObject* obj;
     if (cache && (obj = cache->GetWrapper())) {
 #ifdef DEBUG
       JS::Rooted<JSObject*> rootedObj(cx, obj);
       NS_ASSERTION(WrapNativeISupports(cx, parent, cache) == rootedObj,
                    "Unexpected object in nsWrapperCache");
       obj = rootedObj;
 #endif
-      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+      JS::AssertObjectIsNotGray(obj);
       return obj;
     }
 
     obj = WrapNativeISupports(cx, parent, cache);
-    MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+    JS::AssertObjectIsNotGray(obj);
     return obj;
   }
 };
 
 // Finding the associated global for an object.
 template <typename T>
 static inline JSObject* FindAssociatedGlobal(
     JSContext* cx, T* p, nsWrapperCache* cache,
@@ -1494,47 +1494,47 @@ static inline JSObject* FindAssociatedGl
   if (!p) {
     return JS::CurrentGlobalOrNull(cx);
   }
 
   JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
   if (!obj) {
     return nullptr;
   }
-  MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+  JS::AssertObjectIsNotGray(obj);
 
   // The object is never a CCW but it may not be in the current compartment of
   // the JSContext.
   obj = JS::GetNonCCWObjectGlobal(obj);
 
   switch (scope) {
     case mozilla::dom::ReflectionScope::XBL: {
       // If scope is set to XBLScope, it means that the canonical reflector for
       // this native object should live in the content XBL scope. Note that we
       // never put anonymous content inside an add-on scope.
       if (xpc::IsInContentXBLScope(obj)) {
         return obj;
       }
       JS::Rooted<JSObject*> rootedObj(cx, obj);
       JSObject* xblScope = xpc::GetXBLScope(cx, rootedObj);
       MOZ_ASSERT_IF(xblScope, JS_IsGlobalObject(xblScope));
-      MOZ_ASSERT(JS::ObjectIsNotGray(xblScope));
+      JS::AssertObjectIsNotGray(xblScope);
       return xblScope;
     }
 
     case mozilla::dom::ReflectionScope::UAWidget: {
       // If scope is set to UAWidgetScope, it means that the canonical reflector
       // for this native object should live in the UA widget scope.
       if (xpc::IsInUAWidgetScope(obj)) {
         return obj;
       }
       JS::Rooted<JSObject*> rootedObj(cx, obj);
       JSObject* uaWidgetScope = xpc::GetUAWidgetScope(cx, rootedObj);
       MOZ_ASSERT_IF(uaWidgetScope, JS_IsGlobalObject(uaWidgetScope));
-      MOZ_ASSERT(JS::ObjectIsNotGray(uaWidgetScope));
+      JS::AssertObjectIsNotGray(uaWidgetScope);
       return uaWidgetScope;
     }
 
     case ReflectionScope::Content:
       return obj;
   }
 
   MOZ_CRASH("Unknown ReflectionScope variant");
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -133,17 +133,17 @@ class CallbackObject : public nsISupport
   }
 
   /*
    * If the callback is known to be non-gray, then this method can be
    * used instead of CallbackOrNull() to avoid the overhead of
    * ExposeObjectToActiveJS().
    */
   JS::Handle<JSObject*> CallbackKnownNotGray() const {
-    MOZ_ASSERT(JS::ObjectIsNotGray(mCallback));
+    JS::AssertObjectIsNotGray(mCallback);
     return CallbackPreserveColor();
   }
 
   nsIGlobalObject* IncumbentGlobalOrNull() const { return mIncumbentGlobal; }
 
   enum ExceptionHandling {
     // Report any exception and don't throw it to the caller code.
     eReportExceptions,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3766,17 +3766,17 @@ class CGWrapWithCacheMethod(CGAbstractMe
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
 
             JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
             if (!global) {
               return false;
             }
             MOZ_ASSERT(JS_IsGlobalObject(global));
-            MOZ_ASSERT(JS::ObjectIsNotGray(global));
+            JS::AssertObjectIsNotGray(global);
 
             // That might have ended up wrapping us already, due to the wonders
             // of XBL.  Check for that, and bail out as needed.
             aReflector.set(aCache->GetWrapper());
             if (aReflector) {
             #ifdef DEBUG
               AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
             #endif // DEBUG
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -302,20 +302,20 @@ class MOZ_STACK_CLASS CallArgs
                          bool ignoresReturnValue = false) {
     CallArgs args;
     args.clearUsedRval();
     args.argv_ = argv;
     args.argc_ = argc;
     args.constructing_ = constructing;
     args.ignoresReturnValue_ = ignoresReturnValue;
 #ifdef DEBUG
-    MOZ_ASSERT(ValueIsNotGray(args.thisv()));
-    MOZ_ASSERT(ValueIsNotGray(args.calleev()));
+    AssertValueIsNotGray(args.thisv());
+    AssertValueIsNotGray(args.calleev());
     for (unsigned i = 0; i < argc; ++i) {
-      MOZ_ASSERT(ValueIsNotGray(argv[i]));
+      AssertValueIsNotGray(argv[i]);
     }
 #endif
     return args;
   }
 
  public:
   /*
    * Helper for requireAtLeast to report the actual exception.  Public
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -432,17 +432,17 @@ static MOZ_ALWAYS_INLINE bool CellIsMark
     return false;
   }
   return TenuredCellIsMarkedGray(cell);
 }
 
 extern JS_PUBLIC_API bool CellIsMarkedGrayIfKnown(const Cell* cell);
 
 #ifdef DEBUG
-extern JS_PUBLIC_API bool CellIsNotGray(const Cell* cell);
+extern JS_PUBLIC_API void AssertCellIsNotGray(const Cell* cell);
 
 extern JS_PUBLIC_API bool ObjectIsMarkedBlack(const JSObject* obj);
 #endif
 
 MOZ_ALWAYS_INLINE ChunkLocation GetCellLocation(const void* cell) {
   uintptr_t addr = uintptr_t(cell);
   addr &= ~js::gc::ChunkMask;
   addr |= js::gc::ChunkLocationOffset;
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -166,22 +166,20 @@ struct GCPolicy<jsid> {
   }
   static bool isValid(jsid id) {
     return !JSID_IS_GCTHING(id) ||
            js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
   }
 };
 
 #ifdef DEBUG
-MOZ_ALWAYS_INLINE bool IdIsNotGray(jsid id) {
-  if (!JSID_IS_GCTHING(id)) {
-    return true;
+MOZ_ALWAYS_INLINE void AssertIdIsNotGray(jsid id) {
+  if (JSID_IS_GCTHING(id)) {
+    AssertCellIsNotGray(JSID_TO_GCTHING(id).asCell());
   }
-
-  return CellIsNotGray(JSID_TO_GCTHING(id).asCell());
 }
 #endif
 
 }  // namespace JS
 
 namespace js {
 
 template <>
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -519,24 +519,31 @@ inline const Value& GetProxyReservedSlot
   return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n];
 }
 
 inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) {
   detail::GetProxyDataLayout(obj)->handler = handler;
 }
 
 inline void SetProxyReservedSlot(JSObject* obj, size_t n, const Value& extra) {
-  MOZ_ASSERT_IF(gc::detail::ObjectIsMarkedBlack(obj),
-                JS::ValueIsNotGray(extra));
+#ifdef DEBUG
+  if (gc::detail::ObjectIsMarkedBlack(obj)) {
+    JS::AssertValueIsNotGray(extra);
+  }
+#endif
+
   detail::SetProxyReservedSlotUnchecked(obj, n, extra);
 }
 
 inline void SetProxyPrivate(JSObject* obj, const Value& value) {
-  MOZ_ASSERT_IF(gc::detail::ObjectIsMarkedBlack(obj),
-                JS::ValueIsNotGray(value));
+#ifdef DEBUG
+  if (gc::detail::ObjectIsMarkedBlack(obj)) {
+    JS::AssertValueIsNotGray(value);
+  }
+#endif
 
   Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot;
 
   // Trigger a barrier before writing the slot.
   if (vp->isGCThing() || value.isGCThing()) {
     detail::SetValueInProxy(vp, value);
   } else {
     *vp = value;
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -359,34 +359,42 @@ static MOZ_ALWAYS_INLINE bool ObjectIsMa
   return js::gc::detail::CellIsMarkedGrayIfKnown(cell);
 }
 
 static MOZ_ALWAYS_INLINE bool ObjectIsMarkedGray(
     const JS::Heap<JSObject*>& obj) {
   return ObjectIsMarkedGray(obj.unbarrieredGet());
 }
 
-// The following *IsNotGray functions are for use in assertions and take account
-// of the eventual gray marking state at the end of any ongoing incremental GC.
+// The following *IsNotGray functions take account of the eventual
+// gray marking state at the end of any ongoing incremental GC by
+// delaying the checks if necessary.
+
 #ifdef DEBUG
-inline bool CellIsNotGray(js::gc::Cell* maybeCell) {
-  if (!maybeCell) {
-    return true;
+
+inline void AssertCellIsNotGray(js::gc::Cell* maybeCell) {
+  if (maybeCell) {
+    js::gc::detail::AssertCellIsNotGray(maybeCell);
   }
-
-  return js::gc::detail::CellIsNotGray(maybeCell);
 }
 
-inline bool ObjectIsNotGray(JSObject* maybeObj) {
-  return CellIsNotGray(reinterpret_cast<js::gc::Cell*>(maybeObj));
+inline void AssertObjectIsNotGray(JSObject* maybeObj) {
+  AssertCellIsNotGray(reinterpret_cast<js::gc::Cell*>(maybeObj));
 }
 
-inline bool ObjectIsNotGray(const JS::Heap<JSObject*>& obj) {
-  return ObjectIsNotGray(obj.unbarrieredGet());
+inline void AssertObjectIsNotGray(const JS::Heap<JSObject*>& obj) {
+  AssertObjectIsNotGray(obj.unbarrieredGet());
 }
+
+#else
+
+inline void AssertCellIsNotGray(js::gc::Cell* maybeCell) {}
+inline void AssertObjectIsNotGray(JSObject* maybeObj) {}
+inline void AssertObjectIsNotGray(const JS::Heap<JSObject*>& obj) {}
+
 #endif
 
 /**
  * The TenuredHeap<T> class is similar to the Heap<T> class above in that it
  * encapsulates the GC concerns of an on-heap reference to a JS object. However,
  * it has two important differences:
  *
  *  1) Pointers which are statically known to only reference "tenured" objects
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1365,26 +1365,24 @@ static inline JS::Value PoisonedObjectVa
   return v;
 }
 
 }  // namespace js
 
 #ifdef DEBUG
 namespace JS {
 
-MOZ_ALWAYS_INLINE bool ValueIsNotGray(const Value& value) {
-  if (!value.isGCThing()) {
-    return true;
+MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) {
+  if (value.isGCThing()) {
+    AssertCellIsNotGray(value.toGCThing());
   }
-
-  return CellIsNotGray(value.toGCThing());
 }
 
-MOZ_ALWAYS_INLINE bool ValueIsNotGray(const Heap<Value>& value) {
-  return ValueIsNotGray(value.unbarrieredGet());
+MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) {
+  AssertValueIsNotGray(value.unbarrieredGet());
 }
 
 }  // namespace JS
 #endif
 
 /************************************************************************/
 
 namespace JS {
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -50,17 +50,17 @@ void HeapSlot::assertPreconditionForWrit
   } else {
     uint32_t numShifted = obj->getElementsHeader()->numShiftedElements();
     MOZ_ASSERT(slot >= numShifted);
     MOZ_ASSERT(
         static_cast<HeapSlot*>(obj->getDenseElements() + (slot - numShifted))
             ->get() == target);
   }
 
-  CheckTargetIsNotGray(obj);
+  AssertTargetIsNotGray(obj);
 }
 
 bool CurrentThreadIsIonCompiling() { return TlsContext.get()->ionCompiling; }
 
 bool CurrentThreadIsIonCompilingSafeForMinorGC() {
   return TlsContext.get()->ionCompilingSafeForMinorGC;
 }
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -261,17 +261,17 @@ struct InternalBarrierMethods<T*> {
 
   static void postBarrier(T** vp, T* prev, T* next) {
     T::writeBarrierPost(vp, prev, next);
   }
 
   static void readBarrier(T* v) { T::readBarrier(v); }
 
 #ifdef DEBUG
-  static bool thingIsNotGray(T* v) { return T::thingIsNotGray(v); }
+  static void assertThingIsNotGray(T* v) { return T::assertThingIsNotGray(v); }
 #endif
 };
 
 template <typename S>
 struct PreBarrierFunctor : public VoidDefaultAdaptor<S> {
   template <typename T>
   void operator()(T* t);
 };
@@ -317,36 +317,41 @@ struct InternalBarrierMethods<Value> {
     }
   }
 
   static void readBarrier(const Value& v) {
     DispatchTyped(ReadBarrierFunctor<Value>(), v);
   }
 
 #ifdef DEBUG
-  static bool thingIsNotGray(const Value& v) { return JS::ValueIsNotGray(v); }
+  static void assertThingIsNotGray(const Value& v) {
+    JS::AssertValueIsNotGray(v);
+  }
 #endif
 };
 
 template <>
 struct InternalBarrierMethods<jsid> {
   static bool isMarkable(jsid id) { return JSID_IS_GCTHING(id); }
   static void preBarrier(jsid id) {
     DispatchTyped(PreBarrierFunctor<jsid>(), id);
   }
   static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 #ifdef DEBUG
-  static bool thingIsNotGray(jsid id) { return JS::IdIsNotGray(id); }
+  static void assertThingIsNotGray(jsid id) { JS::AssertIdIsNotGray(id); }
 #endif
 };
 
 template <typename T>
-static inline void CheckTargetIsNotGray(const T& v) {
-  MOZ_ASSERT(InternalBarrierMethods<T>::thingIsNotGray(v) ||
-             CurrentThreadIsTouchingGrayThings());
+static inline void AssertTargetIsNotGray(const T& v) {
+#ifdef DEBUG
+  if (!CurrentThreadIsTouchingGrayThings()) {
+    InternalBarrierMethods<T>::assertThingIsNotGray(v);
+  }
+#endif
 }
 
 // Base class of all barrier types.
 //
 // This is marked non-memmovable since post barriers added by derived classes
 // can add pointers to class instances to the store buffer.
 template <typename T>
 class MOZ_NON_MEMMOVABLE BarrieredBase {
@@ -432,17 +437,17 @@ class PreBarriered : public WriteBarrier
     this->pre();
     this->value = nullptr;
   }
 
   DECLARE_POINTER_ASSIGN_OPS(PreBarriered, T);
 
  private:
   void set(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     this->pre();
     this->value = v;
   }
 };
 
 /*
  * A pre- and post-barriered heap pointer, for use inside the JS engine.
  *
@@ -478,26 +483,26 @@ class GCPtr : public WriteBarrieredBase<
     MOZ_ASSERT(CurrentThreadIsGCSweeping() ||
                this->value == JS::SafelyInitialized<T>());
     Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this),
            MemCheckKind::MakeNoAccess);
   }
 #endif
 
   void init(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     this->value = v;
     this->post(JS::SafelyInitialized<T>(), v);
   }
 
   DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
 
  private:
   void set(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     this->pre();
     T tmp = this->value;
     this->value = v;
     this->post(tmp, this->value);
   }
 
   /*
    * Unlike HeapPtr<T>, GCPtr<T> must be managed with GC lifetimes.
@@ -552,37 +557,37 @@ class HeapPtr : public WriteBarrieredBas
   }
 
   ~HeapPtr() {
     this->pre();
     this->post(this->value, JS::SafelyInitialized<T>());
   }
 
   void init(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     this->value = v;
     this->post(JS::SafelyInitialized<T>(), this->value);
   }
 
   DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
 
   /* Make this friend so it can access pre() and post(). */
   template <class T1, class T2>
   friend inline void BarrieredSetPair(Zone* zone, HeapPtr<T1*>& v1, T1* val1,
                                       HeapPtr<T2*>& v2, T2* val2);
 
  protected:
   void set(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     this->pre();
     postBarrieredSet(v);
   }
 
   void postBarrieredSet(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     T tmp = this->value;
     this->value = v;
     this->post(tmp, this->value);
   }
 };
 
 // Base class for barriered pointer types that intercept reads and writes.
 template <typename T>
@@ -629,17 +634,17 @@ class ReadBarriered : public ReadBarrier
   // the read barrier of the defunct edge.
   ReadBarriered(ReadBarriered&& v) : ReadBarrieredBase<T>(std::move(v)) {
     this->post(JS::SafelyInitialized<T>(), v.value);
   }
 
   ~ReadBarriered() { this->post(this->value, JS::SafelyInitialized<T>()); }
 
   ReadBarriered& operator=(const ReadBarriered& v) {
-    CheckTargetIsNotGray(v.value);
+    AssertTargetIsNotGray(v.value);
     T prior = this->value;
     this->value = v.value;
     this->post(prior, v.value);
     return *this;
   }
 
   const T& get() const {
     if (InternalBarrierMethods<T>::isMarkable(this->value)) {
@@ -655,17 +660,17 @@ class ReadBarriered : public ReadBarrier
   operator const T&() const { return get(); }
 
   const T& operator->() const { return get(); }
 
   T* unsafeGet() { return &this->value; }
   T const* unsafeGet() const { return &this->value; }
 
   void set(const T& v) {
-    CheckTargetIsNotGray(v);
+    AssertTargetIsNotGray(v);
     T tmp = this->value;
     this->value = v;
     this->post(tmp, v);
   }
 };
 
 // A WeakRef pointer does not hold its target live and is automatically nulled
 // out when the GC discovers that it is not reachable from any other path.
@@ -796,17 +801,17 @@ class ImmutableTenuredPtr {
  public:
   operator T() const { return value; }
   T operator->() const { return value; }
 
   operator Handle<T>() const { return Handle<T>::fromMarkedLocation(&value); }
 
   void init(T ptr) {
     MOZ_ASSERT(ptr->isTenured());
-    CheckTargetIsNotGray(ptr);
+    AssertTargetIsNotGray(ptr);
     value = ptr;
   }
 
   T get() const { return value; }
   const T* address() { return &value; }
 };
 
 template <typename T>
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -129,17 +129,17 @@ struct alignas(gc::CellAlignBytes) Cell 
   inline const T* as() const {
     // |this|-qualify the |is| call below to avoid compile errors with even
     // fairly recent versions of gcc, e.g. 7.1.1 according to bz.
     MOZ_ASSERT(this->is<T>());
     return static_cast<const T*>(this);
   }
 
 #ifdef DEBUG
-  static inline bool thingIsNotGray(Cell* cell);
+  static inline void assertThingIsNotGray(Cell* cell);
   inline bool isAligned() const;
   void dump(GenericPrinter& out) const;
   void dump() const;
 #endif
 
  protected:
   uintptr_t address() const;
   inline Chunk* chunk() const;
@@ -425,18 +425,18 @@ static MOZ_ALWAYS_INLINE void AssertVali
 
 /* static */ MOZ_ALWAYS_INLINE void TenuredCell::writeBarrierPost(
     void* cellp, TenuredCell* prior, TenuredCell* next) {
   AssertValidToSkipBarrier(next);
 }
 
 #ifdef DEBUG
 
-/* static */ bool Cell::thingIsNotGray(Cell* cell) {
-  return JS::CellIsNotGray(cell);
+/* static */ void Cell::assertThingIsNotGray(Cell* cell) {
+  JS::AssertCellIsNotGray(cell);
 }
 
 bool Cell::isAligned() const {
   if (!isTenured()) {
     return true;
   }
   return asTenured().isAligned();
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -8922,33 +8922,33 @@ JS_PUBLIC_API bool js::gc::detail::CellI
     return false;
   }
 
   return detail::CellIsMarkedGray(tc);
 }
 
 #ifdef DEBUG
 
-JS_PUBLIC_API bool js::gc::detail::CellIsNotGray(const Cell* cell) {
+JS_PUBLIC_API void js::gc::detail::AssertCellIsNotGray(const Cell* cell) {
   // Check that a cell is not marked gray.
   //
   // Since this is a debug-only check, take account of the eventual mark state
   // of cells that will be marked black by the next GC slice in an incremental
   // GC. For performance reasons we don't do this in CellIsMarkedGrayIfKnown.
 
   if (!CanCheckGrayBits(cell)) {
-    return true;
+    return;
   }
 
   // TODO: I'd like to AssertHeapIsIdle() here, but this ends up getting
   // called during GC and while iterating the heap for memory reporting.
   MOZ_ASSERT(!JS::RuntimeHeapIsCycleCollecting());
 
   auto tc = &cell->asTenured();
-  return !detail::CellIsMarkedGray(tc);
+  MOZ_ASSERT(!detail::CellIsMarkedGray(tc));
 }
 
 extern JS_PUBLIC_API bool js::gc::detail::ObjectIsMarkedBlack(
     const JSObject* obj) {
   return obj->isMarkedBlack();
 }
 
 #endif
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -485,25 +485,25 @@ bool TestCCWs() {
   cx->runtime()->gc.startDebugGC(GC_NORMAL, budget);
   CHECK(JS::IsIncrementalGCInProgress(cx));
   CHECK(wrapper->zone()->isGCMarkingBlackOnly());
   CHECK(!target->zone()->wasGCStarted());
   CHECK(!IsMarkedBlack(wrapper));
   CHECK(!IsMarkedGray(wrapper));
   CHECK(IsMarkedGray(target));
 
-  // Betweeen GC slices: source marked black by barrier, target is still
-  // gray. Target will be marked gray eventually. ObjectIsMarkedGray() is
-  // conservative and reports that target is not marked gray; ObjectIsNotGray
-  // reports the actual state.
+  // Betweeen GC slices: source marked black by barrier, target is
+  // still gray. Target will be marked gray
+  // eventually. ObjectIsMarkedGray() is conservative and reports
+  // that target is not marked gray; AssertObjectIsNotGray() will
+  // assert.
   grayRoots.grayRoot1.get();
   CHECK(IsMarkedBlack(wrapper));
   CHECK(IsMarkedGray(target));
   CHECK(!JS::ObjectIsMarkedGray(target));
-  MOZ_ASSERT(!JS::ObjectIsNotGray(target));
 
   // Final state: source and target are black.
   JS::FinishIncrementalGC(cx, JS::gcreason::API);
   CHECK(IsMarkedBlack(wrapper));
   CHECK(IsMarkedBlack(target));
 
   grayRoots.grayRoot1 = nullptr;
   grayRoots.grayRoot2 = nullptr;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -705,17 +705,17 @@ static void ReleaseAssertObjectHasNoWrap
 JS_PUBLIC_API JSObject* JS_TransplantObject(JSContext* cx, HandleObject origobj,
                                             HandleObject target) {
   AssertHeapIsIdle();
   MOZ_ASSERT(origobj != target);
   MOZ_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
   MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
   MOZ_ASSERT(origobj->getClass() == target->getClass());
   ReleaseAssertObjectHasNoWrappers(cx, target);
-  MOZ_ASSERT(JS::CellIsNotGray(target));
+  JS::AssertCellIsNotGray(target);
 
   RootedValue origv(cx, ObjectValue(*origobj));
   RootedObject newIdentity(cx);
 
   // Don't allow a compacting GC to observe any intermediate state.
   AutoDisableCompactingGC nocgc(cx);
 
   AutoDisableProxyCheck adpc;
@@ -769,17 +769,17 @@ JS_PUBLIC_API JSObject* JS_TransplantObj
     if (!origobj->compartment()->putWrapper(
             cx, CrossCompartmentKey(newIdentity), origv)) {
       MOZ_CRASH();
     }
   }
 
   // The new identity object might be one of several things. Return it to avoid
   // ambiguity.
-  MOZ_ASSERT(JS::CellIsNotGray(newIdentity));
+  JS::AssertCellIsNotGray(newIdentity);
   return newIdentity;
 }
 
 /*
  * Recompute all cross-compartment wrappers for an object, resetting state.
  * Gecko uses this to clear Xray wrappers when doing a navigation that reuses
  * the inner window and global object.
  */
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -303,22 +303,24 @@ JSObject* Wrapper::wrappedObject(JSObjec
 
   if (target) {
     // A cross-compartment wrapper should never wrap a CCW. We rely on this
     // in the wrapper handlers (we use AutoRealm on our return value, and
     // AutoRealm cannot be used with CCWs).
     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(wrapper),
                   !IsCrossCompartmentWrapper(target));
 
+#ifdef DEBUG
     // An incremental GC will eventually mark the targets of black wrappers
     // black but while it is in progress we can observe gray targets.
-    MOZ_ASSERT_IF(
-        !wrapper->runtimeFromMainThread()->gc.isIncrementalGCInProgress() &&
-            wrapper->isMarkedBlack(),
-        JS::ObjectIsNotGray(target));
+    if (!wrapper->runtimeFromMainThread()->gc.isIncrementalGCInProgress() &&
+        wrapper->isMarkedBlack()) {
+      JS::AssertObjectIsNotGray(target);
+    }
+#endif
 
     // Unmark wrapper targets that should be black in case an incremental GC
     // hasn't marked them the correct color yet.
     if (!wrapper->isMarkedGray()) {
       JS::ExposeObjectToActiveJS(target);
     }
   }
 
--- a/js/src/vm/Compartment-inl.h
+++ b/js/src/vm/Compartment-inl.h
@@ -78,17 +78,17 @@ inline bool JS::Compartment::wrap(JSCont
    * script. Unwrap and prewrap are both steps that we take to get to the
    * identity of an incoming objects, and as such, they shuld never map
    * one identity object to another object. This means that we can safely
    * check the cache immediately, and only risk false negatives. Do this
    * in opt builds, and do both in debug builds so that we can assert
    * that we get the same answer.
    */
 #ifdef DEBUG
-  MOZ_ASSERT(JS::ValueIsNotGray(vp));
+  JS::AssertValueIsNotGray(vp);
   JS::RootedObject cacheResult(cx);
 #endif
   JS::RootedValue v(cx, vp);
   if (js::WrapperMap::Ptr p =
           crossCompartmentWrappers.lookup(js::CrossCompartmentKey(v))) {
 #ifdef DEBUG
     cacheResult = &p->value().get().toObject();
 #else
--- a/js/src/vm/Compartment.cpp
+++ b/js/src/vm/Compartment.cpp
@@ -304,17 +304,17 @@ bool Compartment::wrap(JSContext* cx, Mu
   if (!obj) {
     return true;
   }
 
   AutoDisableProxyCheck adpc;
 
   // Anything we're wrapping has already escaped into script, so must have
   // been unmarked-gray at some point in the past.
-  MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+  JS::AssertObjectIsNotGray(obj);
 
   // The passed object may already be wrapped, or may fit a number of special
   // cases that we need to check for and manually correct.
   if (!getNonWrapperObjectForCurrentCompartment(cx, obj)) {
     return false;
   }
 
   // If the reification above did not result in a same-compartment object,
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -436,17 +436,17 @@ class Debugger : private mozilla::Linked
   };
 
   // Barrier methods so we can have ReadBarriered<Debugger*>.
   static void readBarrier(Debugger* dbg) {
     InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
   }
   static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
 #ifdef DEBUG
-  static bool thingIsNotGray(Debugger* dbg) { return true; }
+  static void assertThingIsNotGray(Debugger* dbg) { return; }
 #endif
 
  private:
   GCPtrNativeObject object; /* The Debugger object. Strong reference. */
   WeakGlobalObjectSet
       debuggees; /* Debuggee globals. Cross-compartment weak references. */
   JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
   js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -65,17 +65,17 @@ class ContextChecks {
   void check(JS::Zone* z, int argIndex) {
     if (zone() && z != zone()) {
       fail(zone(), z, argIndex);
     }
   }
 
   void check(JSObject* obj, int argIndex) {
     if (obj) {
-      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+      JS::AssertObjectIsNotGray(obj);
       MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
       check(obj->compartment(), argIndex);
     }
   }
 
   template <typename T>
   void checkAtom(T* thing, int argIndex) {
     static_assert(mozilla::IsSame<T, JSAtom>::value ||
@@ -90,17 +90,17 @@ class ContextChecks {
         MOZ_CRASH_UNSAFE_PRINTF(
             "*** Atom not marked for zone %p at argument %d", zone(), argIndex);
       }
     }
 #endif
   }
 
   void check(JSString* str, int argIndex) {
-    MOZ_ASSERT(JS::CellIsNotGray(str));
+    JS::AssertCellIsNotGray(str);
     if (str->isAtom()) {
       checkAtom(&str->asAtom(), argIndex);
     } else {
       check(str->zone(), argIndex);
     }
   }
 
   void check(JS::Symbol* symbol, int argIndex) { checkAtom(symbol, argIndex); }
@@ -154,17 +154,17 @@ class ContextChecks {
     } else if (JSID_IS_SYMBOL(id)) {
       checkAtom(JSID_TO_SYMBOL(id), argIndex);
     } else {
       MOZ_ASSERT(!JSID_IS_GCTHING(id));
     }
   }
 
   void check(JSScript* script, int argIndex) {
-    MOZ_ASSERT(JS::CellIsNotGray(script));
+    JS::AssertCellIsNotGray(script);
     if (script) {
       check(script->realm(), argIndex);
     }
   }
 
   void check(AbstractFramePtr frame, int argIndex);
 
   void check(Handle<PropertyDescriptor> desc, int argIndex) {
@@ -380,27 +380,27 @@ inline void JSContext::setZone(js::Zone*
     MOZ_ASSERT(!zone_->wasGCStarted());
     freeLists_ = atomsZoneFreeLists_;
   } else {
     freeLists_ = &zone_->arenas.freeLists();
   }
 }
 
 inline void JSContext::enterRealmOf(JSObject* target) {
-  MOZ_ASSERT(JS::CellIsNotGray(target));
+  JS::AssertCellIsNotGray(target);
   enterRealm(target->nonCCWRealm());
 }
 
 inline void JSContext::enterRealmOf(JSScript* target) {
-  MOZ_ASSERT(JS::CellIsNotGray(target));
+  JS::AssertCellIsNotGray(target);
   enterRealm(target->realm());
 }
 
 inline void JSContext::enterRealmOf(js::ObjectGroup* target) {
-  MOZ_ASSERT(JS::CellIsNotGray(target));
+  JS::AssertCellIsNotGray(target);
   enterRealm(target->realm());
 }
 
 inline void JSContext::enterNullRealm() {
   // We should never enter a realm while in the atoms zone.
   MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
 
   setRealm(nullptr);
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -82,38 +82,36 @@ struct MovableCellHasher<TaggedProto> {
   static bool match(const Key& k, const Lookup& l) {
     return k.isDynamic() == l.isDynamic() && k.isObject() == l.isObject() &&
            (!k.isObject() ||
             MovableCellHasher<JSObject*>::match(k.toObject(), l.toObject()));
   }
 };
 
 #ifdef DEBUG
-MOZ_ALWAYS_INLINE bool TaggedProtoIsNotGray(const TaggedProto& proto) {
-  if (!proto.isObject()) {
-    return true;
+MOZ_ALWAYS_INLINE void AssertTaggedProtoIsNotGray(const TaggedProto& proto) {
+  if (proto.isObject()) {
+    JS::AssertObjectIsNotGray(proto.toObject());
   }
-
-  return JS::ObjectIsNotGray(proto.toObject());
 }
 #endif
 
 template <>
 struct InternalBarrierMethods<TaggedProto> {
   static void preBarrier(TaggedProto& proto);
 
   static void postBarrier(TaggedProto* vp, TaggedProto prev, TaggedProto next);
 
   static void readBarrier(const TaggedProto& proto);
 
   static bool isMarkable(const TaggedProto& proto) { return proto.isObject(); }
 
 #ifdef DEBUG
-  static bool thingIsNotGray(const TaggedProto& proto) {
-    return TaggedProtoIsNotGray(proto);
+  static void assertThingIsNotGray(const TaggedProto& proto) {
+    AssertTaggedProtoIsNotGray(proto);
   }
 #endif
 };
 
 template <class Wrapper>
 class WrappedPtrOperations<TaggedProto, Wrapper> {
   const TaggedProto& value() const {
     return static_cast<const Wrapper*>(this)->get();
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -90,17 +90,17 @@ JSObject* WrapperFactory::WaiveXray(JSCo
   RootedObject obj(cx, objArg);
   obj = UncheckedUnwrap(obj);
   MOZ_ASSERT(!js::IsWindow(obj));
 
   JSObject* waiver = GetXrayWaiver(obj);
   if (!waiver) {
     waiver = CreateXrayWaiver(cx, obj);
   }
-  MOZ_ASSERT(JS::ObjectIsNotGray(waiver));
+  JS::AssertObjectIsNotGray(waiver);
   return waiver;
 }
 
 /* static */ bool WrapperFactory::AllowWaiver(JS::Compartment* target,
                                               JS::Compartment* origin) {
   return CompartmentPrivate::Get(target)->allowWaivers &&
          CompartmentOriginInfo::Subsumes(target, origin);
 }
@@ -299,17 +299,17 @@ void WrapperFactory::PrepareForWrapping(
       cx, wrapScope, wn->Native(), nullptr, &NS_GET_IID(nsISupports), false,
       &v);
   if (NS_FAILED(rv)) {
     return;
   }
 
   obj.set(&v.toObject());
   MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
-  MOZ_ASSERT(JS::ObjectIsNotGray(obj), "Should never return gray reflectors");
+  JS::AssertObjectIsNotGray(obj);  // We should never return gray reflectors.
 
   // Because the underlying native didn't have a PreCreate hook, we had
   // to a new (or possibly pre-existing) XPCWN in our compartment.
   // This could be a problem for chrome code that passes XPCOM objects
   // across compartments, because the effects of QI would disappear across
   // compartments.
   //
   // So whenever we pull an XPCWN across compartments in this manner, we