this repo has no description
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