this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "handles.h"
3
4#include <vector>
5
6#include "benchmark/benchmark.h"
7#include "gtest/gtest.h"
8
9#include "objects.h"
10#include "runtime.h"
11#include "test-utils.h"
12#include "visitor.h"
13
14// Put `USE()` around expression or variables to avoid unused variable warnings.
15#define USE(x) static_cast<void>(x)
16
17namespace py {
18namespace testing {
19
20using HandlesTest = RuntimeFixture;
21
22TEST_F(HandlesTest, UpCastTest) {
23 HandleScope scope(thread_);
24
25 SmallInt h1(&scope, RawObject{0xFEEDFACEL});
26
27 Object h2(&scope, *h1);
28 USE(h2);
29
30 RememberingVisitor visitor;
31 thread_->handles()->visitPointers(&visitor);
32 EXPECT_EQ(visitor.count(), 2);
33 EXPECT_TRUE(visitor.hasVisited(*h1));
34}
35
36TEST_F(HandlesTest, DownCastTest) {
37 HandleScope scope(thread_);
38
39 RawObject i1{0xFEEDFACEL};
40 Object h1(&scope, i1);
41
42 SmallInt h2(&scope, *h1);
43 USE(h2);
44
45 RememberingVisitor visitor;
46 thread_->handles()->visitPointers(&visitor);
47 EXPECT_EQ(visitor.count(), 2);
48 EXPECT_TRUE(visitor.hasVisited(i1));
49}
50
51TEST_F(HandlesTest, IllegalCastRunTimeTest) {
52 HandleScope scope(thread_);
53
54 RawObject i1{0xFEEDFACEL};
55 Object h1(&scope, i1);
56
57 EXPECT_DEBUG_DEATH(USE(Dict(&scope, *h1)), "Invalid Handle construction");
58}
59
60TEST_F(HandlesTest, ThreadSubclassTest) {
61 HandleScope scope(thread_);
62
63 Layout layout(&scope, runtime_->layoutAt(LayoutId::kStopIteration));
64 BaseException exn(&scope, runtime_->newInstance(layout));
65 EXPECT_TRUE(exn.isStopIteration());
66}
67
68TEST_F(HandlesTest, IllegalCastWithThreadTest) {
69 HandleScope scope(thread_);
70
71 EXPECT_DEBUG_DEATH(USE(BaseException(&scope, SmallInt::fromWord(123))),
72 "Invalid Handle construction");
73}
74
75TEST_F(HandlesTest, VisitNoScopes) {
76 Handles handles;
77 RememberingVisitor visitor;
78 handles.visitPointers(&visitor);
79 EXPECT_EQ(visitor.count(), 0);
80}
81
82TEST_F(HandlesTest, VisitEmptyScope) {
83 RememberingVisitor visitor;
84 thread_->handles()->visitPointers(&visitor);
85 EXPECT_EQ(visitor.count(), 0);
86}
87
88TEST_F(HandlesTest, VisitOneHandle) {
89 HandleScope scope(thread_);
90 RawObject object{0xFEEDFACEL};
91 Object handle(&scope, object);
92 USE(handle);
93 RememberingVisitor visitor;
94 thread_->handles()->visitPointers(&visitor);
95 EXPECT_EQ(visitor.count(), 1);
96 EXPECT_TRUE(visitor.hasVisited(object));
97}
98
99TEST_F(HandlesTest, VisitTwoHandles) {
100 HandleScope scope(thread_);
101 RememberingVisitor visitor;
102 RawObject o1{0xFEEDFACEL};
103 Object h1(&scope, o1);
104 USE(h1);
105 RawObject o2{0xFACEFEEDL};
106 Object h2(&scope, o2);
107 USE(h2);
108 thread_->handles()->visitPointers(&visitor);
109 EXPECT_EQ(visitor.count(), 2);
110 EXPECT_TRUE(visitor.hasVisited(o1));
111 EXPECT_TRUE(visitor.hasVisited(o2));
112}
113
114TEST_F(HandlesTest, VisitObjectInNestedScope) {
115 Handles* handles = Thread::current()->handles();
116
117 RawObject object{0xFEEDFACEL};
118 {
119 HandleScope s1(thread_);
120 USE(s1);
121 {
122 // No handles have been created so s1 should be empty.
123 RememberingVisitor visitor;
124 handles->visitPointers(&visitor);
125 EXPECT_EQ(visitor.count(), 0);
126 EXPECT_FALSE(visitor.hasVisited(object));
127 }
128 {
129 HandleScope s2(thread_);
130 Object handle(&s2, object);
131 USE(handle);
132 {
133 // Check that one handle has been allocated (in the inner scope).
134 RememberingVisitor visitor;
135 handles->visitPointers(&visitor);
136 EXPECT_EQ(visitor.count(), 1);
137 EXPECT_TRUE(visitor.hasVisited(object));
138 }
139 }
140 {
141 // No handles should be present now the s2 has been popped.
142 RememberingVisitor visitor;
143 handles->visitPointers(&visitor);
144 EXPECT_EQ(visitor.count(), 0);
145 EXPECT_FALSE(visitor.hasVisited(object));
146 }
147 }
148 {
149 // All scopes are gone so there should still be no handles.
150 RememberingVisitor visitor;
151 handles->visitPointers(&visitor);
152 EXPECT_EQ(visitor.count(), 0);
153 EXPECT_FALSE(visitor.hasVisited(object));
154 }
155}
156
157TEST_F(HandlesTest, NestedScopes) {
158 Handles* handles = thread_->handles();
159
160 RawObject o1{0xDECAF1L};
161 RawObject o2{0xDECAF2L};
162 RawObject o3{0xDECAF3L};
163
164 {
165 HandleScope s1(thread_);
166 Object h1(&s1, o1);
167 USE(h1);
168 {
169 // Check scope s1 for objects o1.
170 RememberingVisitor visitor;
171 handles->visitPointers(&visitor);
172 EXPECT_EQ(visitor.count(), 1);
173 EXPECT_TRUE(visitor.hasVisited(o1));
174 EXPECT_FALSE(visitor.hasVisited(o2));
175 EXPECT_FALSE(visitor.hasVisited(o3));
176 }
177 {
178 // Push scope 2.
179 HandleScope s2(thread_);
180 Object h2(&s2, o2);
181 USE(h2);
182 {
183 // Check s2 for o1 and o2.
184 RememberingVisitor visitor;
185 handles->visitPointers(&visitor);
186 EXPECT_EQ(visitor.count(), 2);
187 EXPECT_TRUE(visitor.hasVisited(o1));
188 EXPECT_TRUE(visitor.hasVisited(o2));
189 EXPECT_FALSE(visitor.hasVisited(o3));
190 }
191 }
192 // (Scope 2 is now popped.)
193 {
194 // Push scope 3 (at the depth previously occupied by s2).
195 HandleScope s3(thread_);
196 Object h3(&s3, o3);
197 USE(h3);
198 {
199 // Check s2 for o1 and o3 (but not o2).
200 RememberingVisitor visitor;
201 handles->visitPointers(&visitor);
202 EXPECT_EQ(visitor.count(), 2);
203 EXPECT_TRUE(visitor.hasVisited(o1));
204 EXPECT_FALSE(visitor.hasVisited(o2));
205 EXPECT_TRUE(visitor.hasVisited(o3));
206 }
207 }
208 // (Scope 3 is now popped.)
209 {
210 // Check scope s1 for o1.
211 RememberingVisitor visitor;
212 handles->visitPointers(&visitor);
213 EXPECT_EQ(visitor.count(), 1);
214 EXPECT_TRUE(visitor.hasVisited(o1));
215 EXPECT_FALSE(visitor.hasVisited(o2));
216 EXPECT_FALSE(visitor.hasVisited(o3));
217 }
218 }
219 // (Scope 1 is now popped)
220 {
221 // All of the handles should be gone.
222 RememberingVisitor visitor;
223 handles->visitPointers(&visitor);
224 EXPECT_EQ(visitor.count(), 0);
225 EXPECT_FALSE(visitor.hasVisited(o1));
226 EXPECT_FALSE(visitor.hasVisited(o2));
227 EXPECT_FALSE(visitor.hasVisited(o3));
228 }
229}
230
231// Benchmarks
232class HandleBenchmark : public benchmark::Fixture {
233 public:
234 void SetUp(benchmark::State&) {
235 word heap_size = 32 * kMiB;
236 Interpreter* interpreter = createCppInterpreter();
237 RandomState random_state = randomStateFromSeed(0);
238 runtime_ = new Runtime(heap_size, interpreter, random_state,
239 StdioState::kBuffered);
240 }
241
242 void TearDown(benchmark::State&) { delete runtime_; }
243
244 private:
245 Runtime* runtime_;
246};
247
248BENCHMARK_F(HandleBenchmark, CreationDestruction)(benchmark::State& state) {
249 HandleScope scope(Thread::current());
250
251 RawObject o1{0xFEEDFACEL};
252
253 for (auto _ : state) {
254 Object h1(&scope, o1);
255 USE(h1);
256 }
257}
258
259class NothingVisitor : public PointerVisitor {
260 public:
261 virtual void visitPointer(RawObject*, PointerKind) { visit_count++; }
262
263 int visit_count = 0;
264};
265
266BENCHMARK_F(HandleBenchmark, Visit)(benchmark::State& state) {
267 Handles handles;
268 HandleScope scope(Thread::current());
269
270 Object h1(&scope, RawObject{0xFEEDFACEL});
271 Object h2(&scope, RawObject{0xFEEDFACFL});
272 Object h3(&scope, RawObject{0xFEEDFAD0L});
273 Object h4(&scope, RawObject{0xFEEDFAD1L});
274 Object h5(&scope, RawObject{0xFEEDFAD2L});
275 Object h6(&scope, RawObject{0xFEEDFAD3L});
276 Object h7(&scope, RawObject{0xFEEDFAD4L});
277 Object h8(&scope, RawObject{0xFEEDFAD5L});
278 Object h9(&scope, RawObject{0xFEEDFAD6L});
279 USE(h1);
280 USE(h2);
281 USE(h3);
282 USE(h4);
283 USE(h5);
284 USE(h6);
285 USE(h7);
286 USE(h8);
287 USE(h9);
288
289 NothingVisitor visitor;
290 for (auto _ : state) {
291 handles.visitPointers(&visitor);
292 }
293}
294
295} // namespace testing
296} // namespace py