Bug 1845355 - Add some asserts to detect causes of a crash r=glandium
[gecko.git] / mfbt / tests / TestNonDereferenceable.cpp
blob2f8f7c1dd177861be2fd99d9deeb66cecdcb043f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <utility>
9 #include "mozilla/Assertions.h"
10 #include "mozilla/NonDereferenceable.h"
12 using mozilla::NonDereferenceable;
14 #define CHECK MOZ_RELEASE_ASSERT
16 void TestNonDereferenceableSimple() {
17 // Default construction.
18 NonDereferenceable<int> nd0;
19 CHECK(!nd0);
20 CHECK(!nd0.value());
22 int i = 1;
23 int i2 = 2;
25 // Construction with pointer.
26 NonDereferenceable<int> nd1(&i);
27 CHECK(!!nd1);
28 CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i));
30 // Assignment with pointer.
31 nd1 = &i2;
32 CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i2));
34 // Copy-construction.
35 NonDereferenceable<int> nd2(nd1);
36 CHECK(nd2.value() == reinterpret_cast<uintptr_t>(&i2));
38 // Copy-assignment.
39 nd2 = nd0;
40 CHECK(!nd2.value());
42 // Move-construction.
43 NonDereferenceable<int> nd3{NonDereferenceable<int>(&i)};
44 CHECK(nd3.value() == reinterpret_cast<uintptr_t>(&i));
46 // Move-assignment.
47 nd3 = std::move(nd1);
48 CHECK(nd3.value() == reinterpret_cast<uintptr_t>(&i2));
49 // Note: Not testing nd1's value because we don't want to assume what state
50 // it is left in after move. But at least it should be reusable:
51 nd1 = &i;
52 CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i));
55 void TestNonDereferenceableHierarchy() {
56 struct Base1 {
57 // Member variable, to make sure Base1 is not empty.
58 int x1;
60 struct Base2 {
61 int x2;
63 struct Derived : Base1, Base2 {};
65 Derived d;
67 // Construct NonDereferenceable from raw pointer.
68 NonDereferenceable<Derived> ndd = NonDereferenceable<Derived>(&d);
69 CHECK(ndd);
70 CHECK(ndd.value() == reinterpret_cast<uintptr_t>(&d));
72 // Cast Derived to Base1.
73 NonDereferenceable<Base1> ndb1 = ndd;
74 CHECK(ndb1);
75 CHECK(ndb1.value() == reinterpret_cast<uintptr_t>(static_cast<Base1*>(&d)));
77 // Cast Base1 back to Derived.
78 NonDereferenceable<Derived> nddb1 = ndb1;
79 CHECK(nddb1.value() == reinterpret_cast<uintptr_t>(&d));
81 // Cast Derived to Base2.
82 NonDereferenceable<Base2> ndb2 = ndd;
83 CHECK(ndb2);
84 CHECK(ndb2.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
85 // Sanity check that Base2 should be offset from the start of Derived.
86 CHECK(ndb2.value() != ndd.value());
88 // Cast Base2 back to Derived.
89 NonDereferenceable<Derived> nddb2 = ndb2;
90 CHECK(nddb2.value() == reinterpret_cast<uintptr_t>(&d));
92 // Note that it's not possible to jump between bases, as they're not obviously
93 // related, i.e.: `NonDereferenceable<Base2> ndb22 = ndb1;` doesn't compile.
94 // However it's possible to explicitly navigate through the derived object:
95 NonDereferenceable<Base2> ndb22 = NonDereferenceable<Derived>(ndb1);
96 CHECK(ndb22.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
98 // Handling nullptr; should stay nullptr even for offset bases.
99 ndd = nullptr;
100 CHECK(!ndd);
101 CHECK(!ndd.value());
102 ndb1 = ndd;
103 CHECK(!ndb1);
104 CHECK(!ndb1.value());
105 ndb2 = ndd;
106 CHECK(!ndb2);
107 CHECK(!ndb2.value());
108 nddb2 = ndb2;
109 CHECK(!nddb2);
110 CHECK(!nddb2.value());
113 template <typename T, size_t Index>
114 struct CRTPBase {
115 // Convert `this` from `CRTPBase*` to `T*` while construction is still in
116 // progress; normally UBSan -fsanitize=vptr would catch this, but using
117 // NonDereferenceable should keep UBSan happy.
118 CRTPBase() : mDerived(this) {}
119 NonDereferenceable<T> mDerived;
122 void TestNonDereferenceableCRTP() {
123 struct Derived : CRTPBase<Derived, 1>, CRTPBase<Derived, 2> {};
124 using Base1 = Derived::CRTPBase<Derived, 1>;
125 using Base2 = Derived::CRTPBase<Derived, 2>;
127 Derived d;
128 // Verify that base constructors have correctly captured the address of the
129 // (at the time still incomplete) derived object.
130 CHECK(d.Base1::mDerived.value() == reinterpret_cast<uintptr_t>(&d));
131 CHECK(d.Base2::mDerived.value() == reinterpret_cast<uintptr_t>(&d));
133 // Construct NonDereferenceable from raw pointer.
134 NonDereferenceable<Derived> ndd = NonDereferenceable<Derived>(&d);
135 CHECK(ndd);
136 CHECK(ndd.value() == reinterpret_cast<uintptr_t>(&d));
138 // Cast Derived to Base1.
139 NonDereferenceable<Base1> ndb1 = ndd;
140 CHECK(ndb1);
141 CHECK(ndb1.value() == reinterpret_cast<uintptr_t>(static_cast<Base1*>(&d)));
143 // Cast Base1 back to Derived.
144 NonDereferenceable<Derived> nddb1 = ndb1;
145 CHECK(nddb1.value() == reinterpret_cast<uintptr_t>(&d));
147 // Cast Derived to Base2.
148 NonDereferenceable<Base2> ndb2 = ndd;
149 CHECK(ndb2);
150 CHECK(ndb2.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
151 // Sanity check that Base2 should be offset from the start of Derived.
152 CHECK(ndb2.value() != ndd.value());
154 // Cast Base2 back to Derived.
155 NonDereferenceable<Derived> nddb2 = ndb2;
156 CHECK(nddb2.value() == reinterpret_cast<uintptr_t>(&d));
158 // Note that it's not possible to jump between bases, as they're not obviously
159 // related, i.e.: `NonDereferenceable<Base2> ndb22 = ndb1;` doesn't compile.
160 // However it's possible to explicitly navigate through the derived object:
161 NonDereferenceable<Base2> ndb22 = NonDereferenceable<Derived>(ndb1);
162 CHECK(ndb22.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
165 int main() {
166 TestNonDereferenceableSimple();
167 TestNonDereferenceableHierarchy();
168 TestNonDereferenceableCRTP();
170 return 0;