Bug 1883861 - Part 1: Move visitMemoryBarrier into the common CodeGenerator file...
[gecko.git] / xpcom / tests / gtest / TestNsRefPtr.cpp
blobcd89f2e86f46fee829862432bff6588786232d44
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"
7 #include "nsCOMPtr.h"
8 #include "nsISupports.h"
9 #include "nsQueryObject.h"
10 #include "mozilla/Unused.h"
12 #include "gtest/gtest.h"
14 namespace TestNsRefPtr {
16 #define NS_FOO_IID \
17 { \
18 0x6f7652e0, 0xee43, 0x11d1, { \
19 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
20 } \
23 class Foo : public nsISupports {
24 public:
25 NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID)
27 public:
28 Foo();
29 // virtual dtor because Bar uses our Release()
30 virtual ~Foo();
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 {}
42 int refcount_;
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() {
62 ++refcount_;
63 ++total_addrefs_;
64 return refcount_;
67 MozExternalRefCountType Foo::Release() {
68 int newcount = --refcount_;
69 if (newcount == 0) {
70 delete this;
73 return newcount;
76 nsresult Foo::QueryInterface(const nsIID& aIID, void** aResult) {
77 ++total_queries_;
79 nsISupports* rawPtr = 0;
80 nsresult status = NS_OK;
82 if (aIID.Equals(NS_GET_IID(Foo)))
83 rawPtr = this;
84 else {
85 nsID iid_of_ISupports = NS_ISUPPORTS_IID;
86 if (aIID.Equals(iid_of_ISupports))
87 rawPtr = static_cast<nsISupports*>(this);
88 else
89 status = NS_ERROR_NO_INTERFACE;
92 NS_IF_ADDREF(rawPtr);
93 *aResult = rawPtr;
95 return status;
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;
110 foop->AddRef();
111 *result = foop;
113 return NS_OK;
116 static void set_a_Foo(RefPtr<Foo>* result) {
117 assert(result);
119 RefPtr<Foo> foop(do_QueryObject(new Foo));
120 *result = foop;
123 static RefPtr<Foo> return_a_Foo() {
124 RefPtr<Foo> foop(do_QueryObject(new Foo));
125 return foop;
128 #define NS_BAR_IID \
130 0x6f7652e1, 0xee43, 0x11d1, { \
131 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
135 class Bar : public Foo {
136 public:
137 NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID)
139 public:
140 Bar();
141 ~Bar() override;
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) {
164 ++total_queries_;
166 nsISupports* rawPtr = 0;
167 nsresult status = NS_OK;
169 if (aIID.Equals(NS_GET_IID(Bar)))
170 rawPtr = this;
171 else if (aIID.Equals(NS_GET_IID(Foo)))
172 rawPtr = static_cast<Foo*>(this);
173 else {
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;
181 NS_IF_ADDREF(rawPtr);
182 *aResult = rawPtr;
184 return status;
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
211 // hand?
212 // foop->AddRef();
214 // [Shouldn't compile] Is it a compile time error to try to |Release| be
215 // hand?
216 // foop->Release();
218 // [Shouldn't compile] Is it a compile time error to try to |delete| an
219 // |nsCOMPtr|?
220 // delete foop;
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());
277 foo1p = foo2p;
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);
287 ASSERT_TRUE(foo1p);
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;
300 raw_foo1p->AddRef();
302 auto* raw_foo2p = new Foo;
303 raw_foo2p->AddRef();
305 ASSERT_EQ(Foo::total_addrefs_, 2);
307 RefPtr<Foo> foo1p(dont_AddRef(raw_foo1p));
309 ASSERT_EQ(Foo::total_addrefs_, 2);
311 RefPtr<Foo> foo2p;
312 foo2p = dont_AddRef(raw_foo2p);
314 ASSERT_EQ(Foo::total_addrefs_, 2);
318 // Test that various assignment helpers compile.
319 RefPtr<Foo> foop;
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;
333 RefPtr<Foo> fooP;
334 fooP = do_QueryObject(new Foo);
335 ASSERT_EQ(Foo::total_queries_, 1);
339 RefPtr<Foo> fooP;
340 fooP = do_QueryObject(new Foo);
341 ASSERT_EQ(Foo::total_queries_, 2);
343 RefPtr<Foo> foo2P;
344 foo2P = fooP;
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));
353 ASSERT_TRUE(fooP);
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) \
363 public: \
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; \
373 if (count == 0) { \
374 delete (this); \
375 return 0; \
377 return count; \
380 protected: \
381 mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
383 public:
385 class ObjectForConstPtr {
386 private:
387 // Reference-counted classes cannot have public destructors.
388 ~ObjectForConstPtr() = default;
390 public:
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)
404 RefPtr<Foo> fooP;
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;
428 int test = 1;
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.
443 ASSERT_TRUE(true);