memory-test.c++ (11840B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "memory.h" 23 #include <kj/compat/gtest.h> 24 #include "debug.h" 25 26 namespace kj { 27 namespace { 28 29 TEST(Memory, OwnConst) { 30 Own<int> i = heap<int>(2); 31 EXPECT_EQ(2, *i); 32 33 Own<const int> ci = mv(i); 34 EXPECT_EQ(2, *ci); 35 36 Own<const int> ci2 = heap<const int>(3); 37 EXPECT_EQ(3, *ci2); 38 } 39 40 TEST(Memory, CanConvert) { 41 struct Super { virtual ~Super() {} }; 42 struct Sub: public Super {}; 43 44 static_assert(canConvert<Own<Sub>, Own<Super>>(), "failure"); 45 static_assert(!canConvert<Own<Super>, Own<Sub>>(), "failure"); 46 } 47 48 struct Nested { 49 Nested(bool& destroyed): destroyed(destroyed) {} 50 ~Nested() { destroyed = true; } 51 52 bool& destroyed; 53 Own<Nested> nested; 54 }; 55 56 TEST(Memory, AssignNested) { 57 bool destroyed1 = false, destroyed2 = false; 58 auto nested = heap<Nested>(destroyed1); 59 nested->nested = heap<Nested>(destroyed2); 60 EXPECT_FALSE(destroyed1 || destroyed2); 61 nested = kj::mv(nested->nested); 62 EXPECT_TRUE(destroyed1); 63 EXPECT_FALSE(destroyed2); 64 nested = nullptr; 65 EXPECT_TRUE(destroyed1 && destroyed2); 66 } 67 68 struct DestructionOrderRecorder { 69 DestructionOrderRecorder(uint& counter, uint& recordTo) 70 : counter(counter), recordTo(recordTo) {} 71 ~DestructionOrderRecorder() { 72 recordTo = ++counter; 73 } 74 75 uint& counter; 76 uint& recordTo; 77 }; 78 79 TEST(Memory, Attach) { 80 uint counter = 0; 81 uint destroyed1 = 0; 82 uint destroyed2 = 0; 83 uint destroyed3 = 0; 84 85 auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1); 86 auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2); 87 auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3); 88 89 auto ptr = obj1.get(); 90 91 Own<DestructionOrderRecorder> combined = obj1.attach(kj::mv(obj2), kj::mv(obj3)); 92 93 KJ_EXPECT(combined.get() == ptr); 94 95 KJ_EXPECT(obj1.get() == nullptr); 96 KJ_EXPECT(obj2.get() == nullptr); 97 KJ_EXPECT(obj3.get() == nullptr); 98 KJ_EXPECT(destroyed1 == 0); 99 KJ_EXPECT(destroyed2 == 0); 100 KJ_EXPECT(destroyed3 == 0); 101 102 combined = nullptr; 103 104 KJ_EXPECT(destroyed1 == 1, destroyed1); 105 KJ_EXPECT(destroyed2 == 2, destroyed2); 106 KJ_EXPECT(destroyed3 == 3, destroyed3); 107 } 108 109 TEST(Memory, AttachNested) { 110 uint counter = 0; 111 uint destroyed1 = 0; 112 uint destroyed2 = 0; 113 uint destroyed3 = 0; 114 115 auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1); 116 auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2); 117 auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3); 118 119 auto ptr = obj1.get(); 120 121 Own<DestructionOrderRecorder> combined = obj1.attach(kj::mv(obj2)).attach(kj::mv(obj3)); 122 123 KJ_EXPECT(combined.get() == ptr); 124 125 KJ_EXPECT(obj1.get() == nullptr); 126 KJ_EXPECT(obj2.get() == nullptr); 127 KJ_EXPECT(obj3.get() == nullptr); 128 KJ_EXPECT(destroyed1 == 0); 129 KJ_EXPECT(destroyed2 == 0); 130 KJ_EXPECT(destroyed3 == 0); 131 132 combined = nullptr; 133 134 KJ_EXPECT(destroyed1 == 1, destroyed1); 135 KJ_EXPECT(destroyed2 == 2, destroyed2); 136 KJ_EXPECT(destroyed3 == 3, destroyed3); 137 } 138 139 KJ_TEST("attachRef") { 140 uint counter = 0; 141 uint destroyed1 = 0; 142 uint destroyed2 = 0; 143 uint destroyed3 = 0; 144 145 auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1); 146 auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2); 147 auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3); 148 149 int i = 123; 150 151 Own<int> combined = attachRef(i, kj::mv(obj1), kj::mv(obj2), kj::mv(obj3)); 152 153 KJ_EXPECT(combined.get() == &i); 154 155 KJ_EXPECT(obj1.get() == nullptr); 156 KJ_EXPECT(obj2.get() == nullptr); 157 KJ_EXPECT(obj3.get() == nullptr); 158 KJ_EXPECT(destroyed1 == 0); 159 KJ_EXPECT(destroyed2 == 0); 160 KJ_EXPECT(destroyed3 == 0); 161 162 combined = nullptr; 163 164 KJ_EXPECT(destroyed1 == 1, destroyed1); 165 KJ_EXPECT(destroyed2 == 2, destroyed2); 166 KJ_EXPECT(destroyed3 == 3, destroyed3); 167 } 168 169 KJ_TEST("attachVal") { 170 uint counter = 0; 171 uint destroyed1 = 0; 172 uint destroyed2 = 0; 173 uint destroyed3 = 0; 174 175 auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1); 176 auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2); 177 auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3); 178 179 int i = 123; 180 181 Own<int> combined = attachVal(i, kj::mv(obj1), kj::mv(obj2), kj::mv(obj3)); 182 183 int* ptr = combined.get(); 184 KJ_EXPECT(ptr != &i); 185 KJ_EXPECT(*ptr == i); 186 187 KJ_EXPECT(obj1.get() == nullptr); 188 KJ_EXPECT(obj2.get() == nullptr); 189 KJ_EXPECT(obj3.get() == nullptr); 190 KJ_EXPECT(destroyed1 == 0); 191 KJ_EXPECT(destroyed2 == 0); 192 KJ_EXPECT(destroyed3 == 0); 193 194 combined = nullptr; 195 196 KJ_EXPECT(destroyed1 == 1, destroyed1); 197 KJ_EXPECT(destroyed2 == 2, destroyed2); 198 KJ_EXPECT(destroyed3 == 3, destroyed3); 199 } 200 201 struct StaticType { 202 int i; 203 }; 204 205 struct DynamicType1 { 206 virtual void foo() {} 207 208 int j; 209 210 DynamicType1(int j): j(j) {} 211 }; 212 213 struct DynamicType2 { 214 virtual void bar() {} 215 216 int k; 217 218 DynamicType2(int k): k(k) {} 219 }; 220 221 struct SingularDerivedDynamic final: public DynamicType1 { 222 SingularDerivedDynamic(int j, bool& destructorCalled) 223 : DynamicType1(j), destructorCalled(destructorCalled) {} 224 225 ~SingularDerivedDynamic() { 226 destructorCalled = true; 227 } 228 KJ_DISALLOW_COPY(SingularDerivedDynamic); 229 230 bool& destructorCalled; 231 }; 232 233 struct MultipleDerivedDynamic final: public DynamicType1, public DynamicType2 { 234 MultipleDerivedDynamic(int j, int k, bool& destructorCalled) 235 : DynamicType1(j), DynamicType2(k), destructorCalled(destructorCalled) {} 236 237 ~MultipleDerivedDynamic() { 238 destructorCalled = true; 239 } 240 241 KJ_DISALLOW_COPY(MultipleDerivedDynamic); 242 243 bool& destructorCalled; 244 }; 245 246 TEST(Memory, OwnVoid) { 247 { 248 Own<StaticType> ptr = heap<StaticType>({123}); 249 StaticType* addr = ptr.get(); 250 Own<void> voidPtr = kj::mv(ptr); 251 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 252 } 253 254 { 255 bool destructorCalled = false; 256 Own<SingularDerivedDynamic> ptr = heap<SingularDerivedDynamic>(123, destructorCalled); 257 SingularDerivedDynamic* addr = ptr.get(); 258 Own<void> voidPtr = kj::mv(ptr); 259 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 260 } 261 262 { 263 bool destructorCalled = false; 264 Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled); 265 MultipleDerivedDynamic* addr = ptr.get(); 266 Own<void> voidPtr = kj::mv(ptr); 267 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 268 269 KJ_EXPECT(!destructorCalled); 270 voidPtr = nullptr; 271 KJ_EXPECT(destructorCalled); 272 } 273 274 { 275 bool destructorCalled = false; 276 Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled); 277 MultipleDerivedDynamic* addr = ptr.get(); 278 Own<DynamicType2> basePtr = kj::mv(ptr); 279 DynamicType2* baseAddr = basePtr.get(); 280 281 // On most (all?) C++ ABIs, the second base class in a multiply-inherited class is offset from 282 // the beginning of the object (assuming the first base class has non-zero size). We use this 283 // fact here to verify that then casting to Own<void> does in fact result in a pointer that 284 // points to the start of the overall object, not the base class. We expect that the pointers 285 // are different here to prove that the test below is non-trivial. 286 // 287 // If there is some other ABI where these pointers are the same, and thus this expectation 288 // fails, then it's no problem to #ifdef out the expectation on that platform. 289 KJ_EXPECT(static_cast<void*>(baseAddr) != static_cast<void*>(addr)); 290 291 Own<void> voidPtr = kj::mv(basePtr); 292 KJ_EXPECT(voidPtr.get() == static_cast<void*>(addr)); 293 294 KJ_EXPECT(!destructorCalled); 295 voidPtr = nullptr; 296 KJ_EXPECT(destructorCalled); 297 } 298 } 299 300 TEST(Memory, OwnConstVoid) { 301 { 302 Own<StaticType> ptr = heap<StaticType>({123}); 303 StaticType* addr = ptr.get(); 304 Own<const void> voidPtr = kj::mv(ptr); 305 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 306 } 307 308 { 309 bool destructorCalled = false; 310 Own<SingularDerivedDynamic> ptr = heap<SingularDerivedDynamic>(123, destructorCalled); 311 SingularDerivedDynamic* addr = ptr.get(); 312 Own<const void> voidPtr = kj::mv(ptr); 313 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 314 } 315 316 { 317 bool destructorCalled = false; 318 Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled); 319 MultipleDerivedDynamic* addr = ptr.get(); 320 Own<const void> voidPtr = kj::mv(ptr); 321 KJ_EXPECT(voidPtr.get() == implicitCast<void*>(addr)); 322 323 KJ_EXPECT(!destructorCalled); 324 voidPtr = nullptr; 325 KJ_EXPECT(destructorCalled); 326 } 327 328 { 329 bool destructorCalled = false; 330 Own<MultipleDerivedDynamic> ptr = heap<MultipleDerivedDynamic>(123, 456, destructorCalled); 331 MultipleDerivedDynamic* addr = ptr.get(); 332 Own<DynamicType2> basePtr = kj::mv(ptr); 333 DynamicType2* baseAddr = basePtr.get(); 334 335 // On most (all?) C++ ABIs, the second base class in a multiply-inherited class is offset from 336 // the beginning of the object (assuming the first base class has non-zero size). We use this 337 // fact here to verify that then casting to Own<void> does in fact result in a pointer that 338 // points to the start of the overall object, not the base class. We expect that the pointers 339 // are different here to prove that the test below is non-trivial. 340 // 341 // If there is some other ABI where these pointers are the same, and thus this expectation 342 // fails, then it's no problem to #ifdef out the expectation on that platform. 343 KJ_EXPECT(static_cast<void*>(baseAddr) != static_cast<void*>(addr)); 344 345 Own<const void> voidPtr = kj::mv(basePtr); 346 KJ_EXPECT(voidPtr.get() == static_cast<void*>(addr)); 347 348 KJ_EXPECT(!destructorCalled); 349 voidPtr = nullptr; 350 KJ_EXPECT(destructorCalled); 351 } 352 } 353 354 struct IncompleteType; 355 KJ_DECLARE_NON_POLYMORPHIC(IncompleteType) 356 357 template <typename T, typename U> 358 struct IncompleteTemplate; 359 template <typename T, typename U> 360 KJ_DECLARE_NON_POLYMORPHIC(IncompleteTemplate<T, U>) 361 362 struct IncompleteDisposer: public Disposer { 363 mutable void* sawPtr = nullptr; 364 365 virtual void disposeImpl(void* pointer) const { 366 sawPtr = pointer; 367 } 368 }; 369 370 KJ_TEST("Own<IncompleteType>") { 371 static int i; 372 void* ptr = &i; 373 374 { 375 IncompleteDisposer disposer; 376 377 { 378 kj::Own<IncompleteType> foo(reinterpret_cast<IncompleteType*>(ptr), disposer); 379 kj::Own<IncompleteType> bar = kj::mv(foo); 380 } 381 382 KJ_EXPECT(disposer.sawPtr == ptr); 383 } 384 385 { 386 IncompleteDisposer disposer; 387 388 { 389 kj::Own<IncompleteTemplate<int, char>> foo( 390 reinterpret_cast<IncompleteTemplate<int, char>*>(ptr), disposer); 391 kj::Own<IncompleteTemplate<int, char>> bar = kj::mv(foo); 392 } 393 394 KJ_EXPECT(disposer.sawPtr == ptr); 395 } 396 } 397 398 // TODO(test): More tests. 399 400 } // namespace 401 } // namespace kj