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 "nsCOMPtr.h"
7 #include "gtest/gtest.h"
8 
9 #include "mozilla/Unused.h"
10 
11 #define NS_IFOO_IID \
12 { 0x6f7652e0,  0xee43, 0x11d1, \
13   { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
14 
15 namespace TestCOMPtr
16 {
17 
18 class IFoo : public nsISupports
19 {
20 public:
21   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
22 
23 public:
24   IFoo();
25   // virtual dtor because IBar uses our Release()
26   virtual ~IFoo();
27 
28   NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
29   NS_IMETHOD_(MozExternalRefCountType) Release() override;
30   NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
31 
32   unsigned int refcount_;
33 
34   static int total_constructions_;
35   static int total_destructions_;
36   static int total_queries_;
37 };
38 
39 NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
40 
41 int IFoo::total_constructions_;
42 int IFoo::total_destructions_;
43 int IFoo::total_queries_;
44 
IFoo()45 IFoo::IFoo()
46   : refcount_(0)
47 {
48   ++total_constructions_;
49 }
50 
~IFoo()51 IFoo::~IFoo()
52 {
53   ++total_destructions_;
54 }
55 
56 MozExternalRefCountType
AddRef()57 IFoo::AddRef()
58 {
59   ++refcount_;
60   return refcount_;
61 }
62 
63 MozExternalRefCountType
Release()64 IFoo::Release()
65 {
66   int newcount = --refcount_;
67 
68   if ( newcount == 0 )
69   {
70     delete this;
71   }
72 
73   return newcount;
74 }
75 
76 nsresult
QueryInterface(const nsIID & aIID,void ** aResult)77 IFoo::QueryInterface( const nsIID& aIID, void** aResult )
78 {
79   total_queries_++;
80 
81   nsISupports* rawPtr = 0;
82   nsresult status = NS_OK;
83 
84   if ( aIID.Equals(NS_GET_IID(IFoo)) )
85     rawPtr = this;
86   else
87   {
88     nsID iid_of_ISupports = NS_ISUPPORTS_IID;
89     if ( aIID.Equals(iid_of_ISupports) )
90       rawPtr = static_cast<nsISupports*>(this);
91     else
92       status = NS_ERROR_NO_INTERFACE;
93   }
94 
95   NS_IF_ADDREF(rawPtr);
96   *aResult = rawPtr;
97 
98   return status;
99 }
100 
101 nsresult
CreateIFoo(void ** result)102 CreateIFoo( void** result )
103 // a typical factory function (that calls AddRef)
104 {
105   auto* foop = new IFoo;
106 
107   foop->AddRef();
108   *result = foop;
109 
110   return NS_OK;
111 }
112 
113 void
set_a_IFoo(nsCOMPtr<IFoo> * result)114 set_a_IFoo( nsCOMPtr<IFoo>* result )
115 {
116   nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
117   *result = foop;
118 }
119 
120 nsCOMPtr<IFoo>
return_a_IFoo()121 return_a_IFoo()
122 {
123   nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
124   return foop;
125 }
126 
127 #define NS_IBAR_IID \
128 { 0x6f7652e1,  0xee43, 0x11d1, \
129   { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
130 
131 class IBar : public IFoo
132 {
133 public:
134   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID)
135 
136 public:
137   IBar();
138   ~IBar() override;
139 
140   NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
141 
142   static int total_destructions_;
143   static int total_queries_;
144 };
145 
146 NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID)
147 
148 int IBar::total_destructions_;
149 int IBar::total_queries_;
150 
IBar()151 IBar::IBar()
152 {
153 }
154 
~IBar()155 IBar::~IBar()
156 {
157   total_destructions_++;
158 }
159 
160 nsresult
QueryInterface(const nsID & aIID,void ** aResult)161 IBar::QueryInterface( const nsID& aIID, void** aResult )
162 {
163   total_queries_++;
164 
165   nsISupports* rawPtr = 0;
166   nsresult status = NS_OK;
167 
168   if ( aIID.Equals(NS_GET_IID(IBar)) )
169     rawPtr = this;
170   else if ( aIID.Equals(NS_GET_IID(IFoo)) )
171     rawPtr = static_cast<IFoo*>(this);
172   else
173   {
174     nsID iid_of_ISupports = NS_ISUPPORTS_IID;
175     if ( aIID.Equals(iid_of_ISupports) )
176       rawPtr = static_cast<nsISupports*>(this);
177     else
178       status = NS_ERROR_NO_INTERFACE;
179   }
180 
181   NS_IF_ADDREF(rawPtr);
182   *aResult = rawPtr;
183 
184   return status;
185 }
186 
187 
188 
189 nsresult
CreateIBar(void ** result)190 CreateIBar( void** result )
191   // a typical factory function (that calls AddRef)
192 {
193   auto* barp = new IBar;
194 
195   barp->AddRef();
196   *result = barp;
197 
198   return NS_OK;
199 }
200 
201 void
AnIFooPtrPtrContext(IFoo **)202 AnIFooPtrPtrContext( IFoo** )
203 {
204 }
205 
206 void
AVoidPtrPtrContext(void **)207 AVoidPtrPtrContext( void** )
208 {
209 }
210 
211 void
AnISupportsPtrPtrContext(nsISupports **)212 AnISupportsPtrPtrContext( nsISupports** )
213 {
214 }
215 
216 } // namespace TestCOMPtr
217 
218 using namespace TestCOMPtr;
219 
TEST(COMPtr,Bloat_Raw_Unsafe)220 TEST(COMPtr, Bloat_Raw_Unsafe)
221 {
222   // ER: I'm not sure what this is testing...
223   IBar* barP = 0;
224   nsresult rv = CreateIBar(reinterpret_cast<void**>(&barP));
225   ASSERT_TRUE(NS_SUCCEEDED(rv));
226   ASSERT_TRUE(barP);
227 
228   IFoo* fooP = 0;
229   rv = barP->QueryInterface(NS_GET_IID(IFoo), reinterpret_cast<void**>(&fooP));
230   ASSERT_TRUE(NS_SUCCEEDED(rv));
231   ASSERT_TRUE(fooP);
232 
233   NS_RELEASE(fooP);
234   NS_RELEASE(barP);
235 }
236 
TEST(COMPtr,Bloat_Smart)237 TEST(COMPtr, Bloat_Smart)
238 {
239   // ER: I'm not sure what this is testing...
240   nsCOMPtr<IBar> barP;
241   nsresult rv = CreateIBar( getter_AddRefs(barP) );
242   ASSERT_TRUE(NS_SUCCEEDED(rv));
243   ASSERT_TRUE(barP);
244 
245   nsCOMPtr<IFoo> fooP( do_QueryInterface(barP, &rv) );
246   ASSERT_TRUE(NS_SUCCEEDED(rv));
247   ASSERT_TRUE(fooP);
248 }
249 
TEST(COMPtr,AddRefAndRelease)250 TEST(COMPtr, AddRefAndRelease)
251 {
252   IFoo::total_constructions_ = 0;
253   IFoo::total_destructions_ = 0;
254   IBar::total_destructions_ = 0;
255 
256   {
257     nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
258     ASSERT_EQ(foop->refcount_, (unsigned int)1);
259     ASSERT_EQ(IFoo::total_constructions_, 1);
260     ASSERT_EQ(IFoo::total_destructions_, 0);
261 
262     foop = do_QueryInterface(new IFoo);
263     ASSERT_EQ(foop->refcount_, (unsigned int)1);
264     ASSERT_EQ(IFoo::total_constructions_, 2);
265     ASSERT_EQ(IFoo::total_destructions_, 1);
266 
267     // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
268     //foop->AddRef();
269 
270     // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
271     //foop->Release();
272 
273     // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
274     //delete foop;
275 
276     static_cast<IFoo*>(foop)->AddRef();
277     ASSERT_EQ(foop->refcount_, (unsigned int)2);
278     ASSERT_EQ(IFoo::total_constructions_, 2);
279     ASSERT_EQ(IFoo::total_destructions_, 1);
280 
281     static_cast<IFoo*>(foop)->Release();
282     ASSERT_EQ(foop->refcount_, (unsigned int)1);
283     ASSERT_EQ(IFoo::total_constructions_, 2);
284     ASSERT_EQ(IFoo::total_destructions_, 1);
285   }
286 
287   ASSERT_EQ(IFoo::total_constructions_, 2);
288   ASSERT_EQ(IFoo::total_destructions_, 2);
289 
290   {
291     nsCOMPtr<IFoo> foop( do_QueryInterface(new IBar) );
292     mozilla::Unused << foop;
293   }
294 
295   ASSERT_EQ(IBar::total_destructions_, 1);
296 }
297 
Comparison()298 void Comparison()
299 {
300   IFoo::total_constructions_ = 0;
301   IFoo::total_destructions_ = 0;
302 
303   {
304     nsCOMPtr<IFoo> foo1p( do_QueryInterface(new IFoo) );
305     nsCOMPtr<IFoo> foo2p( do_QueryInterface(new IFoo) );
306 
307     ASSERT_EQ(IFoo::total_constructions_, 2);
308 
309     // Test != operator
310     ASSERT_NE(foo1p, foo2p);
311     ASSERT_NE(foo1p, foo2p.get());
312 
313     // Test == operator
314     foo1p = foo2p;
315 
316     ASSERT_EQ(IFoo::total_destructions_, 1);
317 
318     ASSERT_EQ(foo1p, foo2p);
319     ASSERT_EQ(foo2p, foo2p.get());
320     ASSERT_EQ(foo2p.get(), foo2p);
321 
322     // Test () operator
323     ASSERT_TRUE(foo1p);
324 
325     ASSERT_EQ(foo1p->refcount_, (unsigned int)2);
326     ASSERT_EQ(foo2p->refcount_, (unsigned int)2);
327   }
328 
329   ASSERT_EQ(IFoo::total_destructions_, 2);
330 }
331 
DontAddRef()332 void DontAddRef()
333 {
334   {
335     auto* raw_foo1p = new IFoo;
336     raw_foo1p->AddRef();
337 
338     auto* raw_foo2p = new IFoo;
339     raw_foo2p->AddRef();
340 
341     nsCOMPtr<IFoo> foo1p( dont_AddRef(raw_foo1p) );
342     ASSERT_EQ(raw_foo1p, foo1p);
343     ASSERT_EQ(foo1p->refcount_, (unsigned int)1);
344 
345     nsCOMPtr<IFoo> foo2p;
346     foo2p = dont_AddRef(raw_foo2p);
347     ASSERT_EQ(raw_foo2p, foo2p);
348     ASSERT_EQ(foo2p->refcount_, (unsigned int)1);
349   }
350 }
351 
TEST(COMPtr,AssignmentHelpers)352 TEST(COMPtr, AssignmentHelpers)
353 {
354   IFoo::total_constructions_ = 0;
355   IFoo::total_destructions_ = 0;
356 
357   {
358     nsCOMPtr<IFoo> foop;
359     ASSERT_FALSE(foop);
360     CreateIFoo( nsGetterAddRefs<IFoo>(foop) );
361     ASSERT_TRUE(foop);
362   }
363 
364   ASSERT_EQ(IFoo::total_constructions_, 1);
365   ASSERT_EQ(IFoo::total_destructions_, 1);
366 
367   {
368     nsCOMPtr<IFoo> foop;
369     ASSERT_FALSE(foop);
370     CreateIFoo( getter_AddRefs(foop) );
371     ASSERT_TRUE(foop);
372   }
373 
374   ASSERT_EQ(IFoo::total_constructions_, 2);
375   ASSERT_EQ(IFoo::total_destructions_, 2);
376 
377   {
378     nsCOMPtr<IFoo> foop;
379     ASSERT_FALSE(foop);
380     set_a_IFoo(address_of(foop));
381     ASSERT_TRUE(foop);
382 
383     ASSERT_EQ(IFoo::total_constructions_, 3);
384     ASSERT_EQ(IFoo::total_destructions_, 2);
385 
386     foop = return_a_IFoo();
387     ASSERT_TRUE(foop);
388 
389     ASSERT_EQ(IFoo::total_constructions_, 4);
390     ASSERT_EQ(IFoo::total_destructions_, 3);
391   }
392 
393   ASSERT_EQ(IFoo::total_constructions_, 4);
394   ASSERT_EQ(IFoo::total_destructions_, 4);
395 
396   {
397     nsCOMPtr<IFoo> fooP( do_QueryInterface(new IFoo) );
398     ASSERT_TRUE(fooP);
399 
400     ASSERT_EQ(IFoo::total_constructions_, 5);
401     ASSERT_EQ(IFoo::total_destructions_, 4);
402 
403     nsCOMPtr<IFoo> fooP2( fooP.forget() );
404     ASSERT_TRUE(fooP2);
405 
406     ASSERT_EQ(IFoo::total_constructions_, 5);
407     ASSERT_EQ(IFoo::total_destructions_, 4);
408   }
409 
410   ASSERT_EQ(IFoo::total_constructions_, 5);
411   ASSERT_EQ(IFoo::total_destructions_, 5);
412 }
413 
TEST(COMPtr,QueryInterface)414 TEST(COMPtr, QueryInterface)
415 {
416   IFoo::total_queries_ = 0;
417   IBar::total_queries_ = 0;
418 
419   {
420     nsCOMPtr<IFoo> fooP;
421     ASSERT_FALSE(fooP);
422     fooP = do_QueryInterface(new IFoo);
423     ASSERT_TRUE(fooP);
424     ASSERT_EQ(IFoo::total_queries_, 1);
425 
426     nsCOMPtr<IFoo> foo2P;
427 
428     // Test that |QueryInterface| _not_ called when assigning a smart-pointer
429     // of the same type.);
430     foo2P = fooP;
431     ASSERT_EQ(IFoo::total_queries_, 1);
432   }
433 
434   {
435     nsCOMPtr<IBar> barP( do_QueryInterface(new IBar) );
436     ASSERT_EQ(IBar::total_queries_, 1);
437 
438     // Test that |QueryInterface| is called when assigning a smart-pointer of
439     // a different type.
440     nsCOMPtr<IFoo> fooP( do_QueryInterface(barP) );
441     ASSERT_EQ(IBar::total_queries_, 2);
442     ASSERT_EQ(IFoo::total_queries_, 1);
443     ASSERT_TRUE(fooP);
444   }
445 }
446 
TEST(COMPtr,GetterConversions)447 TEST(COMPtr, GetterConversions)
448 {
449   // This is just a compilation test. We add a few asserts to keep gtest happy.
450   {
451     nsCOMPtr<IFoo> fooP;
452     ASSERT_FALSE(fooP);
453 
454     AnIFooPtrPtrContext( getter_AddRefs(fooP) );
455     AVoidPtrPtrContext( getter_AddRefs(fooP) );
456   }
457 
458 
459   {
460     nsCOMPtr<nsISupports> supportsP;
461     ASSERT_FALSE(supportsP);
462 
463     AVoidPtrPtrContext( getter_AddRefs(supportsP) );
464     AnISupportsPtrPtrContext( getter_AddRefs(supportsP) );
465   }
466 }
467