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/. */
6 #include "mozilla/RefPtr.h"
8 #include "nsISupports.h"
9 #include "nsQueryObject.h"
10 #include "mozilla/Unused.h"
12 #include "gtest/gtest.h"
14 namespace TestNsRefPtr
{
18 0x6f7652e0, 0xee43, 0x11d1, { \
19 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
23 class Foo
: public nsISupports
{
25 NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID
)
29 // virtual dtor because Bar uses our Release()
32 NS_IMETHOD_(MozExternalRefCountType
) AddRef() override
;
33 NS_IMETHOD_(MozExternalRefCountType
) Release() override
;
34 NS_IMETHOD
QueryInterface(const nsIID
&, void**) override
;
35 void MemberFunction(int, int*, int&);
36 virtual void VirtualMemberFunction(int, int*, int&);
37 virtual void VirtualConstMemberFunction(int, int*, int&) const;
39 void NonconstMethod() {}
40 void ConstMethod() const {}
44 static int total_constructions_
;
45 static int total_destructions_
;
46 static int total_addrefs_
;
47 static int total_queries_
;
50 NS_DEFINE_STATIC_IID_ACCESSOR(Foo
, NS_FOO_IID
)
52 int Foo::total_constructions_
;
53 int Foo::total_destructions_
;
54 int Foo::total_addrefs_
;
55 int Foo::total_queries_
;
57 Foo::Foo() : refcount_(0) { ++total_constructions_
; }
59 Foo::~Foo() { ++total_destructions_
; }
61 MozExternalRefCountType
Foo::AddRef() {
67 MozExternalRefCountType
Foo::Release() {
68 int newcount
= --refcount_
;
76 nsresult
Foo::QueryInterface(const nsIID
& aIID
, void** aResult
) {
79 nsISupports
* rawPtr
= 0;
80 nsresult status
= NS_OK
;
82 if (aIID
.Equals(NS_GET_IID(Foo
)))
85 nsID iid_of_ISupports
= NS_ISUPPORTS_IID
;
86 if (aIID
.Equals(iid_of_ISupports
))
87 rawPtr
= static_cast<nsISupports
*>(this);
89 status
= NS_ERROR_NO_INTERFACE
;
98 void Foo::MemberFunction(int aArg1
, int* aArgPtr
, int& aArgRef
) {}
100 void Foo::VirtualMemberFunction(int aArg1
, int* aArgPtr
, int& aArgRef
) {}
102 void Foo::VirtualConstMemberFunction(int aArg1
, int* aArgPtr
,
103 int& aArgRef
) const {}
105 static nsresult
CreateFoo(void** result
)
106 // a typical factory function (that calls AddRef)
108 auto* foop
= new Foo
;
116 static void set_a_Foo(RefPtr
<Foo
>* result
) {
119 RefPtr
<Foo
> foop(do_QueryObject(new Foo
));
123 static RefPtr
<Foo
> return_a_Foo() {
124 RefPtr
<Foo
> foop(do_QueryObject(new Foo
));
130 0x6f7652e1, 0xee43, 0x11d1, { \
131 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
135 class Bar
: public Foo
{
137 NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID
)
143 NS_IMETHOD
QueryInterface(const nsIID
&, void**) override
;
145 void VirtualMemberFunction(int, int*, int&) override
;
146 void VirtualConstMemberFunction(int, int*, int&) const override
;
148 static int total_constructions_
;
149 static int total_destructions_
;
150 static int total_queries_
;
153 NS_DEFINE_STATIC_IID_ACCESSOR(Bar
, NS_BAR_IID
)
155 int Bar::total_constructions_
;
156 int Bar::total_destructions_
;
157 int Bar::total_queries_
;
159 Bar::Bar() { ++total_constructions_
; }
161 Bar::~Bar() { ++total_destructions_
; }
163 nsresult
Bar::QueryInterface(const nsID
& aIID
, void** aResult
) {
166 nsISupports
* rawPtr
= 0;
167 nsresult status
= NS_OK
;
169 if (aIID
.Equals(NS_GET_IID(Bar
)))
171 else if (aIID
.Equals(NS_GET_IID(Foo
)))
172 rawPtr
= static_cast<Foo
*>(this);
174 nsID iid_of_ISupports
= NS_ISUPPORTS_IID
;
175 if (aIID
.Equals(iid_of_ISupports
))
176 rawPtr
= static_cast<nsISupports
*>(this);
178 status
= NS_ERROR_NO_INTERFACE
;
181 NS_IF_ADDREF(rawPtr
);
187 void Bar::VirtualMemberFunction(int aArg1
, int* aArgPtr
, int& aArgRef
) {}
188 void Bar::VirtualConstMemberFunction(int aArg1
, int* aArgPtr
,
189 int& aArgRef
) const {}
191 } // namespace TestNsRefPtr
193 using namespace TestNsRefPtr
;
195 TEST(nsRefPtr
, AddRefAndRelease
)
197 Foo::total_constructions_
= 0;
198 Foo::total_destructions_
= 0;
201 RefPtr
<Foo
> foop(do_QueryObject(new Foo
));
202 ASSERT_EQ(Foo::total_constructions_
, 1);
203 ASSERT_EQ(Foo::total_destructions_
, 0);
204 ASSERT_EQ(foop
->refcount_
, 1);
206 foop
= do_QueryObject(new Foo
);
207 ASSERT_EQ(Foo::total_constructions_
, 2);
208 ASSERT_EQ(Foo::total_destructions_
, 1);
210 // [Shouldn't compile] Is it a compile time error to try to |AddRef| by
214 // [Shouldn't compile] Is it a compile time error to try to |Release| be
218 // [Shouldn't compile] Is it a compile time error to try to |delete| an
222 static_cast<Foo
*>(foop
)->AddRef();
223 ASSERT_EQ(foop
->refcount_
, 2);
225 static_cast<Foo
*>(foop
)->Release();
226 ASSERT_EQ(foop
->refcount_
, 1);
229 ASSERT_EQ(Foo::total_destructions_
, 2);
232 RefPtr
<Foo
> fooP(do_QueryObject(new Foo
));
233 ASSERT_EQ(Foo::total_constructions_
, 3);
234 ASSERT_EQ(Foo::total_destructions_
, 2);
235 ASSERT_EQ(fooP
->refcount_
, 1);
237 Foo::total_addrefs_
= 0;
238 RefPtr
<Foo
> fooP2
= std::move(fooP
);
239 mozilla::Unused
<< fooP2
;
240 ASSERT_EQ(Foo::total_addrefs_
, 0);
244 TEST(nsRefPtr
, VirtualDestructor
)
246 Bar::total_destructions_
= 0;
249 RefPtr
<Foo
> foop(do_QueryObject(new Bar
));
250 mozilla::Unused
<< foop
;
253 ASSERT_EQ(Bar::total_destructions_
, 1);
256 TEST(nsRefPtr
, Equality
)
258 Foo::total_constructions_
= 0;
259 Foo::total_destructions_
= 0;
262 RefPtr
<Foo
> foo1p(do_QueryObject(new Foo
));
263 RefPtr
<Foo
> foo2p(do_QueryObject(new Foo
));
265 ASSERT_EQ(Foo::total_constructions_
, 2);
266 ASSERT_EQ(Foo::total_destructions_
, 0);
268 ASSERT_NE(foo1p
, foo2p
);
270 ASSERT_NE(foo1p
, nullptr);
271 ASSERT_NE(nullptr, foo1p
);
272 ASSERT_FALSE(foo1p
== nullptr);
273 ASSERT_FALSE(nullptr == foo1p
);
275 ASSERT_NE(foo1p
, foo2p
.get());
279 ASSERT_EQ(Foo::total_constructions_
, 2);
280 ASSERT_EQ(Foo::total_destructions_
, 1);
281 ASSERT_EQ(foo1p
, foo2p
);
283 ASSERT_EQ(foo2p
, foo2p
.get());
285 ASSERT_EQ(RefPtr
<Foo
>(foo2p
.get()), foo2p
);
290 ASSERT_EQ(Foo::total_constructions_
, 2);
291 ASSERT_EQ(Foo::total_destructions_
, 2);
294 TEST(nsRefPtr
, AddRefHelpers
)
296 Foo::total_addrefs_
= 0;
299 auto* raw_foo1p
= new Foo
;
302 auto* raw_foo2p
= new Foo
;
305 ASSERT_EQ(Foo::total_addrefs_
, 2);
307 RefPtr
<Foo
> foo1p(dont_AddRef(raw_foo1p
));
309 ASSERT_EQ(Foo::total_addrefs_
, 2);
312 foo2p
= dont_AddRef(raw_foo2p
);
314 ASSERT_EQ(Foo::total_addrefs_
, 2);
318 // Test that various assignment helpers compile.
320 CreateFoo(RefPtrGetterAddRefs
<Foo
>(foop
));
321 CreateFoo(getter_AddRefs(foop
));
322 set_a_Foo(address_of(foop
));
323 foop
= return_a_Foo();
327 TEST(nsRefPtr
, QueryInterface
)
329 Foo::total_queries_
= 0;
330 Bar::total_queries_
= 0;
334 fooP
= do_QueryObject(new Foo
);
335 ASSERT_EQ(Foo::total_queries_
, 1);
340 fooP
= do_QueryObject(new Foo
);
341 ASSERT_EQ(Foo::total_queries_
, 2);
345 ASSERT_EQ(Foo::total_queries_
, 2);
349 RefPtr
<Bar
> barP(do_QueryObject(new Bar
));
350 ASSERT_EQ(Bar::total_queries_
, 1);
352 RefPtr
<Foo
> fooP(do_QueryObject(barP
));
354 ASSERT_EQ(Foo::total_queries_
, 2);
355 ASSERT_EQ(Bar::total_queries_
, 2);
359 // -------------------------------------------------------------------------
360 // TODO(ER): The following tests should be moved to MFBT.
362 #define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class) \
364 NS_METHOD_(MozExternalRefCountType) AddRef(void) const { \
365 MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
366 MOZ_RELEASE_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
367 nsrefcnt count = ++mRefCnt; \
368 return (nsrefcnt)count; \
370 NS_METHOD_(MozExternalRefCountType) Release(void) const { \
371 MOZ_RELEASE_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
372 nsrefcnt count = --mRefCnt; \
381 mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
385 class ObjectForConstPtr
{
387 // Reference-counted classes cannot have public destructors.
388 ~ObjectForConstPtr() = default;
391 NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr
)
392 void ConstMemberFunction(int aArg1
, int* aArgPtr
, int& aArgRef
) const {}
394 #undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
396 namespace TestNsRefPtr
{
397 static void AnFooPtrPtrContext(Foo
**) {}
398 static void AVoidPtrPtrContext(void**) {}
399 } // namespace TestNsRefPtr
401 TEST(nsRefPtr
, RefPtrCompilationTests
)
406 AnFooPtrPtrContext(getter_AddRefs(fooP
));
407 AVoidPtrPtrContext(getter_AddRefs(fooP
));
411 RefPtr
<Foo
> fooP(new Foo
);
412 RefPtr
<const Foo
> constFooP
= fooP
;
413 constFooP
->ConstMethod();
415 // [Shouldn't compile] Is it a compile time error to call a non-const method
416 // on an |RefPtr<const T>|?
417 // constFooP->NonconstMethod();
419 // [Shouldn't compile] Is it a compile time error to construct an |RefPtr<T>
420 // from an |RefPtr<const T>|?
421 // RefPtr<Foo> otherFooP(constFooP);
425 RefPtr
<Foo
> foop
= new Foo
;
426 RefPtr
<Foo
> foop2
= new Bar
;
427 RefPtr
<const ObjectForConstPtr
> foop3
= new ObjectForConstPtr
;
429 void (Foo::*fPtr
)(int, int*, int&) = &Foo::MemberFunction
;
430 void (Foo::*fVPtr
)(int, int*, int&) = &Foo::VirtualMemberFunction
;
431 void (Foo::*fVCPtr
)(int, int*, int&) const =
432 &Foo::VirtualConstMemberFunction
;
433 void (ObjectForConstPtr::*fCPtr2
)(int, int*, int&) const =
434 &ObjectForConstPtr::ConstMemberFunction
;
436 (foop
->*fPtr
)(test
, &test
, test
);
437 (foop2
->*fVPtr
)(test
, &test
, test
);
438 (foop2
->*fVCPtr
)(test
, &test
, test
);
439 (foop3
->*fCPtr2
)(test
, &test
, test
);
442 // Looks like everything ran.