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