1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/RefPtr.h"
7 #include "nsCOMPtr.h"
8 #include "nsISupports.h"
9 #include "nsQueryObject.h"
10 #include "mozilla/Unused.h"
11 
12 #include "gtest/gtest.h"
13 
14 namespace TestNsRefPtr
15 {
16 
17 #define NS_FOO_IID \
18 { 0x6f7652e0,  0xee43, 0x11d1, \
19   { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
20 
21 class Foo : public nsISupports
22 {
23 public:
24   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID)
25 
26 public:
27   Foo();
28   // virtual dtor because Bar uses our Release()
29   virtual ~Foo();
30 
31   NS_IMETHOD_(MozExternalRefCountType) AddRef();
32   NS_IMETHOD_(MozExternalRefCountType) Release();
33   NS_IMETHOD QueryInterface( const nsIID&, void** );
34   void MemberFunction( int, int*, int& );
35   virtual void VirtualMemberFunction( int, int*, int& );
36   virtual void VirtualConstMemberFunction( int, int*, int& ) const;
37 
NonconstMethod()38   void NonconstMethod() {}
ConstMethod() const39   void ConstMethod() const {}
40 
41   int refcount_;
42 
43   static int total_constructions_;
44   static int total_destructions_;
45   static int total_addrefs_;
46   static int total_queries_;
47 };
48 
49 NS_DEFINE_STATIC_IID_ACCESSOR(Foo, NS_FOO_IID)
50 
51 int Foo::total_constructions_;
52 int Foo::total_destructions_;
53 int Foo::total_addrefs_;
54 int Foo::total_queries_;
55 
Foo()56 Foo::Foo()
57   : refcount_(0)
58 {
59   ++total_constructions_;
60 }
61 
~Foo()62 Foo::~Foo()
63 {
64   ++total_destructions_;
65 }
66 
67 MozExternalRefCountType
AddRef()68 Foo::AddRef()
69 {
70   ++refcount_;
71   ++total_addrefs_;
72   return refcount_;
73 }
74 
75 MozExternalRefCountType
Release()76 Foo::Release()
77 {
78   int newcount = --refcount_;
79   if ( newcount == 0 )
80   {
81     delete this;
82   }
83 
84   return newcount;
85 }
86 
87 nsresult
QueryInterface(const nsIID & aIID,void ** aResult)88 Foo::QueryInterface( const nsIID& aIID, void** aResult )
89 {
90   ++total_queries_;
91 
92   nsISupports* rawPtr = 0;
93   nsresult status = NS_OK;
94 
95   if ( aIID.Equals(NS_GET_IID(Foo)) )
96     rawPtr = this;
97   else
98   {
99     nsID iid_of_ISupports = NS_ISUPPORTS_IID;
100     if ( aIID.Equals(iid_of_ISupports) )
101       rawPtr = static_cast<nsISupports*>(this);
102     else
103       status = NS_ERROR_NO_INTERFACE;
104   }
105 
106   NS_IF_ADDREF(rawPtr);
107   *aResult = rawPtr;
108 
109   return status;
110 }
111 
112 void
MemberFunction(int aArg1,int * aArgPtr,int & aArgRef)113 Foo::MemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
114 {
115 }
116 
117 void
VirtualMemberFunction(int aArg1,int * aArgPtr,int & aArgRef)118 Foo::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
119 {
120 }
121 
122 void
VirtualConstMemberFunction(int aArg1,int * aArgPtr,int & aArgRef) const123 Foo::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
124 {
125 }
126 
127 nsresult
CreateFoo(void ** result)128 CreateFoo( void** result )
129   // a typical factory function (that calls AddRef)
130 {
131   Foo* foop = new Foo;
132 
133   foop->AddRef();
134   *result = foop;
135 
136   return NS_OK;
137 }
138 
139 void
set_a_Foo(RefPtr<Foo> * result)140 set_a_Foo( RefPtr<Foo>* result )
141 {
142   assert(result);
143 
144   RefPtr<Foo> foop( do_QueryObject(new Foo) );
145   *result = foop;
146 }
147 
148 RefPtr<Foo>
return_a_Foo()149 return_a_Foo()
150 {
151   RefPtr<Foo> foop( do_QueryObject(new Foo) );
152   return foop;
153 }
154 
155 #define NS_BAR_IID \
156 { 0x6f7652e1,  0xee43, 0x11d1, \
157   { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
158 
159 class Bar : public Foo
160 {
161 public:
162   NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID)
163 
164 public:
165   Bar();
166   virtual ~Bar();
167 
168   NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
169 
170   virtual void VirtualMemberFunction( int, int*, int& ) override;
171   virtual void VirtualConstMemberFunction( int, int*, int& ) const override;
172 
173   static int total_constructions_;
174   static int total_destructions_;
175   static int total_queries_;
176 };
177 
178 NS_DEFINE_STATIC_IID_ACCESSOR(Bar, NS_BAR_IID)
179 
180 int Bar::total_constructions_;
181 int Bar::total_destructions_;
182 int Bar::total_queries_;
183 
Bar()184 Bar::Bar()
185 {
186   ++total_constructions_;
187 }
188 
~Bar()189 Bar::~Bar()
190 {
191   ++total_destructions_;
192 }
193 
194 nsresult
QueryInterface(const nsID & aIID,void ** aResult)195 Bar::QueryInterface( const nsID& aIID, void** aResult )
196 {
197   ++total_queries_;
198 
199   nsISupports* rawPtr = 0;
200   nsresult status = NS_OK;
201 
202   if ( aIID.Equals(NS_GET_IID(Bar)) )
203     rawPtr = this;
204   else if ( aIID.Equals(NS_GET_IID(Foo)) )
205     rawPtr = static_cast<Foo*>(this);
206   else
207   {
208     nsID iid_of_ISupports = NS_ISUPPORTS_IID;
209     if ( aIID.Equals(iid_of_ISupports) )
210       rawPtr = static_cast<nsISupports*>(this);
211     else
212       status = NS_ERROR_NO_INTERFACE;
213   }
214 
215   NS_IF_ADDREF(rawPtr);
216   *aResult = rawPtr;
217 
218   return status;
219 }
220 
221 void
VirtualMemberFunction(int aArg1,int * aArgPtr,int & aArgRef)222 Bar::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
223 {
224 }
225 void
VirtualConstMemberFunction(int aArg1,int * aArgPtr,int & aArgRef) const226 Bar::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
227 {
228 }
229 
230 } // namespace TestNsRefPtr
231 
232 using namespace TestNsRefPtr;
233 
TEST(nsRefPtr,AddRefAndRelease)234 TEST(nsRefPtr, AddRefAndRelease)
235 {
236   Foo::total_constructions_ = 0;
237   Foo::total_destructions_ = 0;
238 
239   {
240     RefPtr<Foo> foop( do_QueryObject(new Foo) );
241     ASSERT_EQ(Foo::total_constructions_, 1);
242     ASSERT_EQ(Foo::total_destructions_, 0);
243     ASSERT_EQ(foop->refcount_, 1);
244 
245     foop = do_QueryObject(new Foo);
246     ASSERT_EQ(Foo::total_constructions_, 2);
247     ASSERT_EQ(Foo::total_destructions_, 1);
248 
249     // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
250     //foop->AddRef();
251 
252     // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
253     //foop->Release();
254 
255     // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
256     //delete foop;
257 
258     static_cast<Foo*>(foop)->AddRef();
259     ASSERT_EQ(foop->refcount_, 2);
260 
261     static_cast<Foo*>(foop)->Release();
262     ASSERT_EQ(foop->refcount_, 1);
263   }
264 
265   ASSERT_EQ(Foo::total_destructions_, 2);
266 
267   {
268     RefPtr<Foo> fooP( do_QueryObject(new Foo) );
269     ASSERT_EQ(Foo::total_constructions_, 3);
270     ASSERT_EQ(Foo::total_destructions_, 2);
271     ASSERT_EQ(fooP->refcount_, 1);
272 
273     Foo::total_addrefs_ = 0;
274     RefPtr<Foo> fooP2( fooP.forget() );
275     ASSERT_EQ(Foo::total_addrefs_, 0);
276   }
277 }
278 
TEST(nsRefPtr,VirtualDestructor)279 TEST(nsRefPtr, VirtualDestructor)
280 {
281   Bar::total_destructions_ = 0;
282 
283   {
284     RefPtr<Foo> foop( do_QueryObject(new Bar) );
285     mozilla::Unused << foop;
286   }
287 
288   ASSERT_EQ(Bar::total_destructions_, 1);
289 }
290 
TEST(nsRefPtr,Equality)291 TEST(nsRefPtr, Equality)
292 {
293   Foo::total_constructions_ = 0;
294   Foo::total_destructions_ = 0;
295 
296   {
297     RefPtr<Foo> foo1p( do_QueryObject(new Foo) );
298     RefPtr<Foo> foo2p( do_QueryObject(new Foo) );
299 
300     ASSERT_EQ(Foo::total_constructions_, 2);
301     ASSERT_EQ(Foo::total_destructions_, 0);
302 
303     ASSERT_NE(foo1p, foo2p);
304 
305     ASSERT_NE(foo1p, nullptr);
306     ASSERT_NE(nullptr, foo1p);
307     ASSERT_FALSE(foo1p == nullptr);
308     ASSERT_FALSE(nullptr == foo1p);
309 
310     ASSERT_NE(foo1p, foo2p.get());
311 
312     foo1p = foo2p;
313 
314     ASSERT_EQ(Foo::total_constructions_, 2);
315     ASSERT_EQ(Foo::total_destructions_, 1);
316     ASSERT_EQ(foo1p, foo2p);
317 
318     ASSERT_EQ(foo2p, foo2p.get());
319 
320     ASSERT_EQ(RefPtr<Foo>(foo2p.get()), foo2p);
321 
322     ASSERT_TRUE(foo1p);
323   }
324 
325   ASSERT_EQ(Foo::total_constructions_, 2);
326   ASSERT_EQ(Foo::total_destructions_, 2);
327 }
328 
TEST(nsRefPtr,AddRefHelpers)329 TEST(nsRefPtr, AddRefHelpers)
330 {
331   Foo::total_addrefs_ = 0;
332 
333   {
334     Foo* raw_foo1p = new Foo;
335     raw_foo1p->AddRef();
336 
337     Foo* raw_foo2p = new Foo;
338     raw_foo2p->AddRef();
339 
340     ASSERT_EQ(Foo::total_addrefs_, 2);
341 
342     RefPtr<Foo> foo1p( dont_AddRef(raw_foo1p) );
343 
344     ASSERT_EQ(Foo::total_addrefs_, 2);
345 
346     RefPtr<Foo> foo2p;
347     foo2p = dont_AddRef(raw_foo2p);
348 
349     ASSERT_EQ(Foo::total_addrefs_, 2);
350   }
351 
352   {
353     // Test that various assignment helpers compile.
354     RefPtr<Foo> foop;
355     CreateFoo( RefPtrGetterAddRefs<Foo>(foop) );
356     CreateFoo( getter_AddRefs(foop) );
357     set_a_Foo(address_of(foop));
358     foop = return_a_Foo();
359   }
360 }
361 
TEST(nsRefPtr,QueryInterface)362 TEST(nsRefPtr, QueryInterface)
363 {
364   Foo::total_queries_ = 0;
365   Bar::total_queries_ = 0;
366 
367   {
368     RefPtr<Foo> fooP;
369     fooP = do_QueryObject(new Foo);
370     ASSERT_EQ(Foo::total_queries_, 1);
371   }
372 
373   {
374     RefPtr<Foo> fooP;
375     fooP = do_QueryObject(new Foo);
376     ASSERT_EQ(Foo::total_queries_, 2);
377 
378     RefPtr<Foo> foo2P;
379     foo2P = fooP;
380     ASSERT_EQ(Foo::total_queries_, 2);
381   }
382 
383   {
384     RefPtr<Bar> barP( do_QueryObject(new Bar) );
385     ASSERT_EQ(Bar::total_queries_, 1);
386 
387     RefPtr<Foo> fooP( do_QueryObject(barP) );
388     ASSERT_TRUE(fooP);
389     ASSERT_EQ(Foo::total_queries_, 2);
390     ASSERT_EQ(Bar::total_queries_, 2);
391   }
392 }
393 
394 // -------------------------------------------------------------------------
395 // TODO(ER): The following tests should be moved to MFBT.
396 
397 #define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class)               \
398 public:                                                                     \
399 NS_METHOD_(MozExternalRefCountType) AddRef(void) const {                    \
400   MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class)                                \
401   MOZ_RELEASE_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");              \
402   nsrefcnt count = ++mRefCnt;                                               \
403   return (nsrefcnt) count;                                                  \
404 }                                                                           \
405 NS_METHOD_(MozExternalRefCountType) Release(void) const {                   \
406   MOZ_RELEASE_ASSERT(int32_t(mRefCnt) > 0, "dup release");                  \
407   nsrefcnt count = --mRefCnt;                                               \
408   if (count == 0) {                                                         \
409     delete (this);                                                          \
410     return 0;                                                               \
411   }                                                                         \
412   return count;                                                             \
413 }                                                                           \
414 protected:                                                                  \
415 mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt;                            \
416 public:
417 
418 class ObjectForConstPtr
419 {
420   private:
421     // Reference-counted classes cannot have public destructors.
~ObjectForConstPtr()422     ~ObjectForConstPtr()
423     {
424     }
425   public:
NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr)426     NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr)
427       void ConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
428       {
429       }
430 };
431 #undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
432 
433 namespace TestNsRefPtr
434 {
AnFooPtrPtrContext(Foo **)435 void AnFooPtrPtrContext(Foo**) { }
AVoidPtrPtrContext(void **)436 void AVoidPtrPtrContext(void**) { }
437 } // namespace TestNsRefPtr
438 
TEST(nsRefPtr,RefPtrCompilationTests)439 TEST(nsRefPtr, RefPtrCompilationTests)
440 {
441 
442   {
443     RefPtr<Foo> fooP;
444 
445     AnFooPtrPtrContext( getter_AddRefs(fooP) );
446     AVoidPtrPtrContext( getter_AddRefs(fooP) );
447   }
448 
449   {
450     RefPtr<Foo> fooP(new Foo);
451     RefPtr<const Foo> constFooP = fooP;
452     constFooP->ConstMethod();
453 
454     // [Shouldn't compile] Is it a compile time error to call a non-const method on an |RefPtr<const T>|?
455     //constFooP->NonconstMethod();
456 
457     // [Shouldn't compile] Is it a compile time error to construct an |RefPtr<T> from an |RefPtr<const T>|?
458     //RefPtr<Foo> otherFooP(constFooP);
459   }
460 
461   {
462     RefPtr<Foo> foop = new Foo;
463     RefPtr<Foo> foop2 = new Bar;
464     RefPtr<const ObjectForConstPtr> foop3 = new ObjectForConstPtr;
465     int test = 1;
466     void (Foo::*fPtr)( int, int*, int& ) = &Foo::MemberFunction;
467     void (Foo::*fVPtr)( int, int*, int& ) = &Foo::VirtualMemberFunction;
468     void (Foo::*fVCPtr)( int, int*, int& ) const = &Foo::VirtualConstMemberFunction;
469     void (ObjectForConstPtr::*fCPtr2)( int, int*, int& ) const = &ObjectForConstPtr::ConstMemberFunction;
470 
471     (foop->*fPtr)(test, &test, test);
472     (foop2->*fVPtr)(test, &test, test);
473     (foop2->*fVCPtr)(test, &test, test);
474     (foop3->*fCPtr2)(test, &test, test);
475   }
476 
477   // Looks like everything ran.
478   ASSERT_TRUE(true);
479 }
480