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