this repo has no description
at trunk 296 lines 7.6 kB view raw
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