this repo has no description
at trunk 438 lines 15 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "scavenger.h" 3 4#include "gtest/gtest.h" 5 6#include "builtins-module.h" 7#include "test-utils.h" 8#include "trampolines.h" 9 10namespace py { 11namespace testing { 12 13using ScavengerTest = RuntimeFixture; 14 15TEST_F(ScavengerTest, PreserveWeakReferenceHeapReferent) { 16 HandleScope scope(thread_); 17 Tuple array(&scope, newTupleWithNone(10)); 18 WeakRef ref(&scope, runtime_->newWeakRef(thread_, array)); 19 runtime_->collectGarbage(); 20 EXPECT_EQ(ref.referent(), *array); 21} 22 23TEST_F(ScavengerTest, ClearWeakReference) { 24 HandleScope scope(Thread::current()); 25 Object none(&scope, NoneType::object()); 26 Object ref(&scope, *none); 27 { 28 Tuple array(&scope, newTupleWithNone(10)); 29 WeakRef ref_inner(&scope, runtime_->newWeakRef(Thread::current(), array)); 30 ref = *ref_inner; 31 runtime_->collectGarbage(); 32 EXPECT_EQ(ref_inner.referent(), *array); 33 } 34 runtime_->collectGarbage(); 35 EXPECT_EQ(WeakRef::cast(*ref).referent(), NoneType::object()); 36} 37 38TEST_F(ScavengerTest, ClearWeakLinkReferences) { 39 HandleScope scope(thread_); 40 Object none(&scope, NoneType::object()); 41 Object link0(&scope, *none); 42 Object link1(&scope, *none); 43 Object link2(&scope, *none); 44 Tuple referent1(&scope, newTupleWithNone(11)); 45 { 46 Tuple referent0(&scope, newTupleWithNone(10)); 47 Tuple referent2(&scope, newTupleWithNone(11)); 48 WeakLink link0_inner(&scope, 49 runtime_->newWeakLink(thread_, referent0, none, none)); 50 WeakLink link1_inner( 51 &scope, runtime_->newWeakLink(thread_, referent1, link0_inner, none)); 52 WeakLink link2_inner( 53 &scope, runtime_->newWeakLink(thread_, referent2, link1_inner, none)); 54 link0_inner.setNext(*link1_inner); 55 link1_inner.setNext(*link2_inner); 56 57 link0 = *link0_inner; 58 link1 = *link1_inner; 59 link2 = *link2_inner; 60 61 runtime_->collectGarbage(); 62 EXPECT_EQ(link0_inner.referent(), *referent0); 63 EXPECT_EQ(link1_inner.referent(), *referent1); 64 EXPECT_EQ(link2_inner.referent(), *referent2); 65 } 66 runtime_->collectGarbage(); 67 EXPECT_EQ(WeakRef::cast(*link0).referent(), NoneType::object()); 68 EXPECT_EQ(WeakRef::cast(*link1).referent(), *referent1); 69 EXPECT_EQ(WeakRef::cast(*link2).referent(), NoneType::object()); 70} 71 72TEST_F(ScavengerTest, PreserveSomeClearSomeReferents) { 73 HandleScope scope(thread_); 74 75 // Create strongly referenced heap allocated objects. 76 MutableTuple strongrefs(&scope, runtime_->newMutableTuple(4)); 77 for (word i = 0; i < strongrefs.length(); i++) { 78 Float elt(&scope, runtime_->newFloat(i)); 79 strongrefs.atPut(i, *elt); 80 } 81 82 // Create a parallel array of weak references with the strongly referenced 83 // objects as referents. 84 MutableTuple weakrefs(&scope, runtime_->newMutableTuple(4)); 85 for (word i = 0; i < weakrefs.length(); i++) { 86 Object obj(&scope, strongrefs.at(i)); 87 WeakRef elt(&scope, runtime_->newWeakRef(thread_, obj)); 88 weakrefs.atPut(i, *elt); 89 } 90 91 // Make sure the weak references point to the expected referents. 92 EXPECT_EQ(strongrefs.at(0), WeakRef::cast(weakrefs.at(0)).referent()); 93 EXPECT_EQ(strongrefs.at(1), WeakRef::cast(weakrefs.at(1)).referent()); 94 EXPECT_EQ(strongrefs.at(2), WeakRef::cast(weakrefs.at(2)).referent()); 95 EXPECT_EQ(strongrefs.at(3), WeakRef::cast(weakrefs.at(3)).referent()); 96 97 // Now do a garbage collection. 98 runtime_->collectGarbage(); 99 100 // Make sure that the weak references still point to the expected referents. 101 EXPECT_EQ(strongrefs.at(0), WeakRef::cast(weakrefs.at(0)).referent()); 102 EXPECT_EQ(strongrefs.at(1), WeakRef::cast(weakrefs.at(1)).referent()); 103 EXPECT_EQ(strongrefs.at(2), WeakRef::cast(weakrefs.at(2)).referent()); 104 EXPECT_EQ(strongrefs.at(3), WeakRef::cast(weakrefs.at(3)).referent()); 105 106 // Clear the odd indexed strong references. 107 strongrefs.atPut(1, NoneType::object()); 108 strongrefs.atPut(3, NoneType::object()); 109 runtime_->collectGarbage(); 110 111 // Now do another garbage collection. This one should clear just the weak 112 // references that point to objects that are no longer strongly referenced. 113 114 // Check that the strongly referenced referents are preserved and the weakly 115 // referenced referents are now cleared. 116 EXPECT_EQ(strongrefs.at(0), WeakRef::cast(weakrefs.at(0)).referent()); 117 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(1)).referent()); 118 EXPECT_EQ(strongrefs.at(2), WeakRef::cast(weakrefs.at(2)).referent()); 119 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(3)).referent()); 120 121 // Clear the even indexed strong references. 122 strongrefs.atPut(0, NoneType::object()); 123 strongrefs.atPut(2, NoneType::object()); 124 125 // Do a final garbage collection. There are no more strongly referenced 126 // objects so all of the weak references should be cleared. 127 runtime_->collectGarbage(); 128 129 // Check that all of the referents are cleared. 130 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(0)).referent()); 131 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(1)).referent()); 132 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(2)).referent()); 133 EXPECT_EQ(NoneType::object(), WeakRef::cast(weakrefs.at(3)).referent()); 134} 135 136TEST_F(ScavengerTest, BaseCallback) { 137 HandleScope scope(thread_); 138 ASSERT_FALSE(runFromCStr(runtime_, R"( 139a = 1 140b = 2 141def f(ref): 142 global a 143 a = 3 144def g(ref, c=4): 145 global b 146 b = c 147)") 148 .isError()); 149 Object none(&scope, NoneType::object()); 150 Object ref1(&scope, *none); 151 Object ref2(&scope, *none); 152 { 153 Tuple array1(&scope, newTupleWithNone(10)); 154 Function func_f(&scope, mainModuleAt(runtime_, "f")); 155 WeakRef ref1_inner( 156 &scope, newWeakRefWithCallback(runtime_, thread_, array1, func_f)); 157 ref1 = *ref1_inner; 158 159 Tuple array2(&scope, newTupleWithNone(10)); 160 Function func_g(&scope, mainModuleAt(runtime_, "g")); 161 WeakRef ref2_inner( 162 &scope, newWeakRefWithCallback(runtime_, thread_, array2, func_g)); 163 ref2 = *ref2_inner; 164 165 runtime_->collectGarbage(); 166 167 EXPECT_EQ(ref1_inner.referent(), *array1); 168 EXPECT_EQ(ref2_inner.referent(), *array2); 169 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 170 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 171 EXPECT_EQ(a.value(), 1); 172 EXPECT_EQ(b.value(), 2); 173 } 174 runtime_->collectGarbage(); 175 176 EXPECT_EQ(WeakRef::cast(*ref1).referent(), NoneType::object()); 177 EXPECT_EQ(WeakRef::cast(*ref1).callback(), NoneType::object()); 178 EXPECT_EQ(WeakRef::cast(*ref2).referent(), NoneType::object()); 179 EXPECT_EQ(WeakRef::cast(*ref2).callback(), NoneType::object()); 180 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 181 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 182 EXPECT_EQ(a.value(), 3); 183 EXPECT_EQ(b.value(), 4); 184} 185 186TEST_F(ScavengerTest, MixCallback) { 187 HandleScope scope(thread_); 188 ASSERT_FALSE(runFromCStr(runtime_, R"( 189a = 1 190b = 2 191def f(ref): 192 global a 193 a = 3 194def g(ref, c=4): 195 global b 196 b = c 197)") 198 .isError()); 199 200 Tuple array1(&scope, newTupleWithNone(10)); 201 Function func_f(&scope, mainModuleAt(runtime_, "f")); 202 WeakRef ref1(&scope, 203 newWeakRefWithCallback(runtime_, thread_, array1, func_f)); 204 Object ref2(&scope, NoneType::object()); 205 { 206 Tuple array2(&scope, newTupleWithNone(10)); 207 Function func_g(&scope, mainModuleAt(runtime_, "g")); 208 WeakRef ref2_inner( 209 &scope, newWeakRefWithCallback(runtime_, thread_, array2, func_g)); 210 ref2 = *ref2_inner; 211 212 runtime_->collectGarbage(); 213 214 EXPECT_EQ(ref1.referent(), *array1); 215 EXPECT_EQ(ref2_inner.referent(), *array2); 216 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 217 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 218 EXPECT_EQ(a.value(), 1); 219 EXPECT_EQ(b.value(), 2); 220 } 221 runtime_->collectGarbage(); 222 223 EXPECT_EQ(ref1.referent(), *array1); 224 EXPECT_EQ(BoundMethod::cast(ref1.callback()).function(), *func_f); 225 EXPECT_EQ(WeakRef::cast(*ref2).referent(), NoneType::object()); 226 EXPECT_EQ(WeakRef::cast(*ref2).callback(), NoneType::object()); 227 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 228 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 229 EXPECT_EQ(a.value(), 1); 230 EXPECT_EQ(b.value(), 4); 231} 232 233static ALIGN_16 RawObject doGarbageCollection(Thread* thread, Arguments) { 234 thread->runtime()->collectGarbage(); 235 return NoneType::object(); 236} 237 238TEST_F(ScavengerTest, CallbackInvokeGC) { 239 HandleScope scope(thread_); 240 ASSERT_FALSE(runFromCStr(runtime_, R"( 241a = 1 242def g(ref, b=2): 243 global a 244 a = b 245)") 246 .isError()); 247 Object ref1(&scope, NoneType::object()); 248 Object ref2(&scope, NoneType::object()); 249 { 250 Tuple array1(&scope, newTupleWithNone(10)); 251 Str name(&scope, runtime_->newStrFromCStr("collect")); 252 Object empty_tuple(&scope, runtime_->emptyTuple()); 253 Code code(&scope, 254 runtime_->newBuiltinCode( 255 /*argcount=*/0, /*posonlyargcount=*/0, 256 /*kwonlyargcount=*/0, /*flags=*/0, doGarbageCollection, 257 /*parameter_names=*/empty_tuple, name)); 258 Module module(&scope, findMainModule(runtime_)); 259 Function collect( 260 &scope, runtime_->newFunctionWithCode(thread_, name, code, module)); 261 262 WeakRef ref1_inner( 263 &scope, newWeakRefWithCallback(runtime_, thread_, array1, collect)); 264 ref1 = *ref1_inner; 265 266 Tuple array2(&scope, newTupleWithNone(10)); 267 Function func_g(&scope, mainModuleAt(runtime_, "g")); 268 WeakRef ref2_inner( 269 &scope, newWeakRefWithCallback(runtime_, thread_, array2, func_g)); 270 ref2 = *ref2_inner; 271 272 runtime_->collectGarbage(); 273 274 EXPECT_EQ(ref1_inner.referent(), *array1); 275 EXPECT_EQ(ref2_inner.referent(), *array2); 276 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 277 EXPECT_EQ(a.value(), 1); 278 } 279 runtime_->collectGarbage(); 280 281 EXPECT_EQ(WeakRef::cast(*ref1).referent(), NoneType::object()); 282 EXPECT_EQ(WeakRef::cast(*ref1).callback(), NoneType::object()); 283 EXPECT_EQ(WeakRef::cast(*ref2).referent(), NoneType::object()); 284 EXPECT_EQ(WeakRef::cast(*ref2).callback(), NoneType::object()); 285 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 286 EXPECT_EQ(a.value(), 2); 287} 288 289TEST_F(ScavengerTest, IgnoreCallbackException) { 290 HandleScope scope(thread_); 291 ASSERT_FALSE(runFromCStr(runtime_, R"( 292a = 1 293b = 2 294callback_ran = False 295callback_returned = False 296def f(ref): 297 global callback_ran 298 callback_ran = True 299 raise AttributeError("aloha") 300 global callback_returned 301 callback_returned = True 302def g(ref, c=4): 303 global b 304 b = c 305)") 306 .isError()); 307 Object ref1(&scope, NoneType::object()); 308 Object ref2(&scope, NoneType::object()); 309 { 310 Tuple array1(&scope, newTupleWithNone(10)); 311 Function func_f(&scope, mainModuleAt(runtime_, "f")); 312 WeakRef ref1_inner( 313 &scope, newWeakRefWithCallback(runtime_, thread_, array1, func_f)); 314 ref1 = *ref1_inner; 315 316 Tuple array2(&scope, newTupleWithNone(10)); 317 Function func_g(&scope, mainModuleAt(runtime_, "g")); 318 WeakRef ref2_inner( 319 &scope, newWeakRefWithCallback(runtime_, thread_, array2, func_g)); 320 ref2 = *ref2_inner; 321 322 runtime_->collectGarbage(); 323 324 EXPECT_EQ(ref1_inner.referent(), *array1); 325 EXPECT_EQ(ref2_inner.referent(), *array2); 326 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 327 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 328 EXPECT_EQ(a.value(), 1); 329 EXPECT_EQ(b.value(), 2); 330 } 331 332 runtime_->collectGarbage(); 333 EXPECT_FALSE(Thread::current()->hasPendingException()); 334 EXPECT_EQ(mainModuleAt(runtime_, "callback_ran"), Bool::trueObj()); 335 EXPECT_EQ(mainModuleAt(runtime_, "callback_returned"), Bool::falseObj()); 336 337 EXPECT_EQ(WeakRef::cast(*ref1).referent(), NoneType::object()); 338 EXPECT_EQ(WeakRef::cast(*ref1).callback(), NoneType::object()); 339 EXPECT_EQ(WeakRef::cast(*ref2).referent(), NoneType::object()); 340 EXPECT_EQ(WeakRef::cast(*ref2).callback(), NoneType::object()); 341 SmallInt a(&scope, mainModuleAt(runtime_, "a")); 342 SmallInt b(&scope, mainModuleAt(runtime_, "b")); 343 EXPECT_EQ(a.value(), 1); 344 EXPECT_EQ(b.value(), 4); 345} 346 347TEST_F(ScavengerTest, CollectGarbagePreservesPendingException) { 348 ASSERT_FALSE(runFromCStr(runtime_, R"( 349did_run = False 350def callback(ref): 351 global did_run 352 did_run = ref 353 raise TabError() 354class C: 355 pass 356)") 357 .isError()); 358 HandleScope scope(thread_); 359 ASSERT_EQ(mainModuleAt(runtime_, "did_run"), Bool::falseObj()); 360 Object callback(&scope, mainModuleAt(runtime_, "callback")); 361 Type c(&scope, mainModuleAt(runtime_, "C")); 362 363 // Create an instance C and a weak reference with callback to it. 364 Layout layout(&scope, c.instanceLayout()); 365 Object instance(&scope, runtime_->newInstance(layout)); 366 WeakRef ref(&scope, 367 newWeakRefWithCallback(runtime_, thread_, instance, callback)); 368 instance = NoneType::object(); 369 370 thread_->raise(LayoutId::kUserWarning, runtime_->newInt(99)); 371 runtime_->collectGarbage(); 372 373 EXPECT_EQ(ref.referent(), NoneType::object()); 374 EXPECT_EQ(mainModuleAt(runtime_, "did_run"), ref); 375 ASSERT_TRUE(thread_->hasPendingException()); 376 EXPECT_TRUE(thread_->pendingExceptionMatches(LayoutId::kUserWarning)); 377 EXPECT_TRUE(isIntEqualsWord(thread_->pendingExceptionValue(), 99)); 378} 379 380TEST_F(ScavengerTest, CollectGarbageCollectsDeadLayout) { 381 ASSERT_FALSE(runFromCStr(runtime_, R"( 382class C: 383 pass 384)") 385 .isError()); 386 387 HandleScope scope(thread_); 388 LayoutId c_layout_id; 389 { 390 Type c(&scope, mainModuleAt(runtime_, "C")); 391 Layout c_layout(&scope, c.instanceLayout()); 392 c_layout_id = c_layout.id(); 393 ASSERT_EQ(runtime_->layoutAt(c_layout_id), *c_layout); 394 } 395 396 ASSERT_FALSE(runFromCStr(runtime_, "del C").isError()); 397 // This collection should kill C. 398 runtime_->collectGarbage(); 399 EXPECT_TRUE(runtime_->layoutAt(c_layout_id) == SmallInt::fromWord(0)); 400} 401 402TEST_F(ScavengerTest, CollectGarbagePreservesTypeWithoutNormalReferences) { 403 ASSERT_FALSE(runFromCStr(runtime_, R"( 404class C: 405 pass 406c = C() 407del C 408)") 409 .isError()); 410 411 runtime_->collectGarbage(); 412 HandleScope scope(thread_); 413 Object c(&scope, mainModuleAt(runtime_, "c")); 414 EXPECT_TRUE(runtime_->typeOf(*c).isType()); 415} 416 417TEST_F(ScavengerTest, CollectGarbagePreservesLayoutWithNoLiveObjects) { 418 ASSERT_FALSE(runFromCStr(runtime_, R"( 419class C: 420 pass 421)") 422 .isError()); 423 424 HandleScope scope(thread_); 425 Type c(&scope, mainModuleAt(runtime_, "C")); 426 LayoutId c_layout_id; 427 { 428 Layout c_layout(&scope, c.instanceLayout()); 429 c_layout_id = c_layout.id(); 430 ASSERT_EQ(runtime_->layoutAt(c_layout_id), *c_layout); 431 } 432 433 runtime_->collectGarbage(); 434 EXPECT_EQ(c.instanceLayout(), runtime_->layoutAt(c_layout_id)); 435} 436 437} // namespace testing 438} // namespace py