Bug 1056356 - Add support for nsRefPtr<const T>. r=froydnj
authorBotond Ballo <botond@mozilla.com>
Wed, 05 Aug 2015 13:52:06 -0400
changeset 288090 eeab332c767c30d0134d8c76d8b4ce6d17d7faaa
parent 288089 f4fa8c49ebc63dba5d1c89740853cb2af39c98fd
child 288091 d22e3da3ebcf475d546986b6f348cf317984ce83
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1056356
milestone42.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 1056356 - Add support for nsRefPtr<const T>. r=froydnj
mfbt/nsRefPtr.h
xpcom/tests/TestNsRefPtr.cpp
--- a/mfbt/nsRefPtr.h
+++ b/mfbt/nsRefPtr.h
@@ -24,17 +24,17 @@ template<class T> class OwningNonNull;
 template <class T>
 class nsRefPtr
 {
 private:
   void
   assign_with_AddRef(T* aRawPtr)
   {
     if (aRawPtr) {
-      aRawPtr->AddRef();
+      AddRefTraits<T>::AddRef(aRawPtr);
     }
     assign_assuming_AddRef(aRawPtr);
   }
 
   void**
   begin_assignment()
   {
     assign_assuming_AddRef(0);
@@ -42,63 +42,63 @@ private:
   }
 
   void
   assign_assuming_AddRef(T* aNewPtr)
   {
     T* oldPtr = mRawPtr;
     mRawPtr = aNewPtr;
     if (oldPtr) {
-      oldPtr->Release();
+      AddRefTraits<T>::Release(oldPtr);
     }
   }
 
 private:
   T* MOZ_OWNING_REF mRawPtr;
 
 public:
   typedef T element_type;
 
   ~nsRefPtr()
   {
     if (mRawPtr) {
-      mRawPtr->Release();
+      AddRefTraits<T>::Release(mRawPtr);
     }
   }
 
   // Constructors
 
   nsRefPtr()
     : mRawPtr(0)
     // default constructor
   {
   }
 
   nsRefPtr(const nsRefPtr<T>& aSmartPtr)
     : mRawPtr(aSmartPtr.mRawPtr)
     // copy-constructor
   {
     if (mRawPtr) {
-      mRawPtr->AddRef();
+      AddRefTraits<T>::AddRef(mRawPtr);
     }
   }
 
   nsRefPtr(nsRefPtr<T>&& aRefPtr)
     : mRawPtr(aRefPtr.mRawPtr)
   {
     aRefPtr.mRawPtr = nullptr;
   }
 
   // construct from a raw pointer (of the right type)
 
   MOZ_IMPLICIT nsRefPtr(T* aRawPtr)
     : mRawPtr(aRawPtr)
   {
     if (mRawPtr) {
-      mRawPtr->AddRef();
+      AddRefTraits<T>::AddRef(mRawPtr);
     }
   }
 
   template <typename I>
   MOZ_IMPLICIT nsRefPtr(already_AddRefed<I>& aSmartPtr)
     : mRawPtr(aSmartPtr.take())
     // construct from |already_AddRefed|
   {
@@ -107,16 +107,26 @@ public:
   template <typename I>
   MOZ_IMPLICIT nsRefPtr(already_AddRefed<I>&& aSmartPtr)
     : mRawPtr(aSmartPtr.take())
     // construct from |otherRefPtr.forget()|
   {
   }
 
   template <typename I>
+  MOZ_IMPLICIT nsRefPtr(const nsRefPtr<I>& aSmartPtr)
+    : mRawPtr(aSmartPtr.get())
+    // copy-construct from a smart pointer with a related pointer type
+  {
+    if (mRawPtr) {
+      AddRefTraits<T>::AddRef(mRawPtr);
+    }
+  }
+
+  template <typename I>
   MOZ_IMPLICIT nsRefPtr(nsRefPtr<I>&& aSmartPtr)
     : mRawPtr(aSmartPtr.forget().take())
     // construct from |Move(nsRefPtr<SomeSubclassOfT>)|.
   {
   }
 
   MOZ_IMPLICIT nsRefPtr(const nsCOMPtr_helper& aHelper);
 
@@ -129,16 +139,25 @@ public:
   nsRefPtr<T>&
   operator=(const nsRefPtr<T>& aRhs)
   // copy assignment operator
   {
     assign_with_AddRef(aRhs.mRawPtr);
     return *this;
   }
 
+  template <typename I>
+  nsRefPtr<T>&
+  operator=(const nsRefPtr<I>& aRhs)
+  // assign from an nsRefPtr of a related pointer type
+  {
+    assign_with_AddRef(aRhs.get());
+    return *this;
+  }
+
   nsRefPtr<T>&
   operator=(T* aRhs)
   // assign from a raw pointer (of the right type)
   {
     assign_with_AddRef(aRhs);
     return *this;
   }
 
@@ -302,16 +321,45 @@ public:
   }
 
   T**
   StartAssignment()
   {
     assign_assuming_AddRef(0);
     return reinterpret_cast<T**>(&mRawPtr);
   }
+private:
+  // This helper class makes |nsRefPtr<const T>| possible by casting away
+  // the constness from the pointer when calling AddRef() and Release().
+  // This is necessary because AddRef() and Release() implementations can't
+  // generally expected to be const themselves (without heavy use of |mutable|
+  // and |const_cast| in their own implementations).
+  // This should be sound because while |nsRefPtr<const T>| provides a const
+  // view of an object, the object itself should be const (it would have to be
+  // allocated as |new const T| or similar to itself be const).
+  template<class U>
+  struct AddRefTraits
+  {
+    static void AddRef(U* aPtr) {
+      aPtr->AddRef();
+    }
+    static void Release(U* aPtr) {
+      aPtr->Release();
+    }
+  };
+  template<class U>
+  struct AddRefTraits<const U>
+  {
+    static void AddRef(const U* aPtr) {
+      const_cast<U*>(aPtr)->AddRef();
+    }
+    static void Release(const U* aPtr) {
+      const_cast<U*>(aPtr)->Release();
+    }
+  };
 };
 
 class nsCycleCollectionTraversalCallback;
 template <typename T>
 void
 CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
                          T* aChild, const char* aName, uint32_t aFlags);
 
--- a/xpcom/tests/TestNsRefPtr.cpp
+++ b/xpcom/tests/TestNsRefPtr.cpp
@@ -27,16 +27,19 @@ class Foo : public nsISupports
       NS_IMETHOD_(MozExternalRefCountType) AddRef();
       NS_IMETHOD_(MozExternalRefCountType) Release();
       NS_IMETHOD QueryInterface( const nsIID&, void** );
       void MemberFunction( int, int*, int& );
       virtual void VirtualMemberFunction( int, int*, int& );
       virtual void VirtualConstMemberFunction( int, int*, int& ) const;
       static void print_totals();
 
+      void NonconstMethod() {}
+      void ConstMethod() const {}
+
     private:
       unsigned int refcount_;
 
       static unsigned int total_constructions_;
       static unsigned int total_destructions_;
   };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Foo, NS_FOO_IID)
@@ -608,18 +611,35 @@ main()
 
 		{
 			nsRefPtr<Foo> fooP;
 
 			AnFooPtrPtrContext( getter_AddRefs(fooP) );
 			AVoidPtrPtrContext( getter_AddRefs(fooP) );
 		}
 
+		{
+		  printf("\n### setup for Test 25\n");
+		  nsRefPtr<Foo> fooP(new Foo);
 
-    printf("\n### Test 25: will a static |nsCOMPtr| |Release| before program termination?\n");
+		  printf("### Test 25: can you construct an |nsRefPtr<const T>| from an |nsRefPtr<T>|?\n");
+		  nsRefPtr<const Foo> constFooP = fooP;
+
+		  printf("### Test 25: can you call a non-const method on an |nsRefPtr<const T>|?\n");
+		  constFooP->ConstMethod();
+
+		  // [Shouldn't compile] Is it a compile time error to call a non-const method on an |nsRefPtr<const T>|?
+		  //constFooP->NonconstMethod();
+
+		  // [Shouldn't compile] Is it a compile time error to construct an |nsRefPtr<T> from an |nsRefPtr<const T>|?
+		  //nsRefPtr<Foo> otherFooP(constFooP);
+		}
+
+
+    printf("\n### Test 26: will a static |nsCOMPtr| |Release| before program termination?\n");
     gFoop = do_QueryObject(new Foo);
 
     {
       printf("\n### setup for Test 26, 27, 28\n");
       nsRefPtr<Foo> foop = new Foo;
       nsRefPtr<Foo> foop2 = new Bar;
       nsRefPtr<const ObjectForConstPtr> foop3 = new ObjectForConstPtr;
       int test = 1;