this repo has no description
at trunk 424 lines 15 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "api-handle.h" 3 4#include "cpython-func.h" 5#include "gtest/gtest.h" 6 7#include "capi-state.h" 8#include "capi.h" 9#include "dict-builtins.h" 10#include "int-builtins.h" 11#include "object-builtins.h" 12#include "runtime.h" 13#include "test-utils.h" 14#include "type-builtins.h" 15 16namespace py { 17namespace testing { 18 19using CApiHandlesDeathTest = RuntimeFixture; 20using ApiHandleTest = RuntimeFixture; 21 22static RawObject initializeExtensionType(PyObject* extension_type) { 23 Thread* thread = Thread::current(); 24 Runtime* runtime = thread->runtime(); 25 HandleScope scope(thread); 26 27 Str name(&scope, runtime->newStrFromCStr("ExtType")); 28 Object object_type(&scope, runtime->typeAt(LayoutId::kObject)); 29 Tuple bases(&scope, runtime->newTupleWith1(object_type)); 30 Dict dict(&scope, runtime->newDict()); 31 Type metaclass(&scope, runtime->typeAt(LayoutId::kType)); 32 Type type(&scope, typeNew(thread, metaclass, name, bases, dict, 33 Type::Flag::kHasNativeData, 34 /*inherit_slots=*/false, 35 /*add_instance_dict=*/false)); 36 37 extension_type->reference_ = type.raw(); 38 return *type; 39} 40 41static ApiHandle* findHandleMatching(Runtime* runtime, 42 const char* str_contents) { 43 class Visitor : public HandleVisitor { 44 public: 45 void visitHandle(void* handle, RawObject object) override { 46 if (object.isStr() && Str::cast(object).equalsCStr(str_contents)) { 47 found = handle; 48 } 49 } 50 const char* str_contents; 51 void* found = nullptr; 52 }; 53 Visitor visitor; 54 visitor.str_contents = str_contents; 55 visitApiHandles(runtime, &visitor); 56 return reinterpret_cast<ApiHandle*>(visitor.found); 57} 58 59TEST_F(ApiHandleTest, ManagedObjectHandleWithRefcountZeroIsDisposed) { 60 HandleScope scope(thread_); 61 Object object(&scope, runtime_->newStrFromCStr("hello world")); 62 63 ApiHandle* handle = ApiHandle::newReference(runtime_, *object); 64 ASSERT_FALSE(ApiHandle::isImmediate(handle)); 65 ASSERT_FALSE(ApiHandle::isBorrowedNoImmediate(handle)); 66 EXPECT_EQ(ApiHandle::refcnt(handle), 1); 67 68 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 69 ApiHandle::decref(handle); 70 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), nullptr); 71} 72 73TEST_F(ApiHandleTest, ManagedObjectHandleWithRefcountZeroAfterGCIsDisposed) { 74 HandleScope scope(thread_); 75 Object object(&scope, runtime_->newStrFromCStr("hello world")); 76 77 ApiHandle* handle = ApiHandle::newReference(runtime_, *object); 78 ASSERT_FALSE(ApiHandle::isImmediate(handle)); 79 ASSERT_FALSE(ApiHandle::isBorrowedNoImmediate(handle)); 80 EXPECT_EQ(ApiHandle::refcnt(handle), 1); 81 82 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 83 runtime_->collectGarbage(); 84 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 85 ApiHandle::decref(handle); 86 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), nullptr); 87} 88 89TEST_F(ApiHandleTest, ManagedObjectHandleBorrowedIsNotDisposed) { 90 HandleScope scope(thread_); 91 Object object(&scope, runtime_->newStrFromCStr("hello world")); 92 93 ApiHandle* handle = ApiHandle::borrowedReference(runtime_, *object); 94 ASSERT_FALSE(ApiHandle::isImmediate(handle)); 95 ASSERT_TRUE(ApiHandle::isBorrowedNoImmediate(handle)); 96 EXPECT_EQ(ApiHandle::refcnt(handle), 0); 97 98 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 99 runtime_->collectGarbage(); 100 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 101} 102 103TEST_F(ApiHandleTest, ManagedObjectHandleBorrowedIsDisposedByGC) { 104 HandleScope scope(thread_); 105 Object object(&scope, runtime_->newStrFromCStr("hello world")); 106 107 ApiHandle* handle = ApiHandle::borrowedReference(runtime_, *object); 108 ASSERT_FALSE(ApiHandle::isImmediate(handle)); 109 ASSERT_TRUE(ApiHandle::isBorrowedNoImmediate(handle)); 110 EXPECT_EQ(ApiHandle::refcnt(handle), 0); 111 112 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 113 object = NoneType::object(); 114 runtime_->collectGarbage(); 115 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), nullptr); 116} 117 118TEST_F(ApiHandleTest, ManagedObjectHandleCachedIsDisposedByGC) { 119 HandleScope scope(thread_); 120 Object object(&scope, runtime_->newStrFromCStr("hello world")); 121 122 ApiHandle* handle = ApiHandle::newReference(runtime_, *object); 123 ASSERT_FALSE(ApiHandle::isImmediate(handle)); 124 ASSERT_FALSE(ApiHandle::isBorrowedNoImmediate(handle)); 125 ASSERT_EQ(ApiHandle::refcnt(handle), 1); 126 127 EXPECT_EQ(capiCaches(runtime_)->at(*object), nullptr); 128 const char* as_utf8 = PyUnicode_AsUTF8(handle); 129 EXPECT_EQ(capiCaches(runtime_)->at(*object), as_utf8); 130 EXPECT_TRUE(ApiHandle::isBorrowedNoImmediate(handle)); 131 ApiHandle::decref(handle); 132 ASSERT_EQ(ApiHandle::refcnt(handle), 0); 133 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 134 135 // Check that handle is not disposed while still references by `object`. 136 runtime_->collectGarbage(); 137 138 EXPECT_EQ(capiCaches(runtime_)->at(*object), as_utf8); 139 EXPECT_TRUE(ApiHandle::isBorrowedNoImmediate(handle)); 140 EXPECT_EQ(std::strcmp(as_utf8, "hello world"), 0); 141 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), handle); 142 143 // Check that handle is disposed when last reference in `object` disappears. 144 object = NoneType::object(); 145 runtime_->collectGarbage(); 146 EXPECT_EQ(findHandleMatching(runtime_, "hello world"), nullptr); 147} 148 149TEST_F(ApiHandleTest, BorrowedApiHandles) { 150 HandleScope scope(thread_); 151 152 // Create a new object and a new reference to that object. 153 Object obj(&scope, runtime_->newList()); 154 ApiHandle* new_ref = ApiHandle::newReference(runtime_, *obj); 155 word refcnt = ApiHandle::refcnt(new_ref); 156 157 // Create a borrowed reference to the same object. This should not affect the 158 // reference count of the handle. 159 ApiHandle* borrowed_ref = ApiHandle::borrowedReference(runtime_, *obj); 160 EXPECT_EQ(borrowed_ref, new_ref); 161 EXPECT_EQ(ApiHandle::refcnt(borrowed_ref), refcnt); 162 163 // Create another new reference. This should increment the reference count 164 // of the handle. 165 ApiHandle* another_ref = ApiHandle::newReference(runtime_, *obj); 166 EXPECT_EQ(another_ref, new_ref); 167 EXPECT_EQ(ApiHandle::refcnt(another_ref), refcnt + 1); 168} 169 170TEST_F(ApiHandleTest, BuiltinHeapAllocatedIntObjectReturnsApiHandle) { 171 HandleScope scope(thread_); 172 Object obj(&scope, runtime_->newInt(SmallInt::kMaxValue + 1)); 173 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 174 EXPECT_NE(handle, nullptr); 175 EXPECT_FALSE(ApiHandle::isImmediate(handle)); 176 ApiHandleDict* dict = capiHandles(runtime_); 177 EXPECT_EQ(dict->at(*obj), handle); 178 ApiHandle::decref(handle); 179} 180 181TEST_F(ApiHandleTest, BuiltinImmediateIntObjectReturnsImmediateApiHandle) { 182 HandleScope scope(thread_); 183 Object obj(&scope, runtime_->newInt(1)); 184 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 185 EXPECT_NE(handle, nullptr); 186 EXPECT_TRUE(ApiHandle::isImmediate(handle)); 187 ApiHandleDict* dict = capiHandles(runtime_); 188 EXPECT_EQ(dict->at(*obj), nullptr); 189 ApiHandle::decref(handle); 190} 191 192TEST_F(ApiHandleTest, BuiltinImmediateTrueObjectReturnsImmediateApiHandle) { 193 HandleScope scope(thread_); 194 Object obj(&scope, Bool::trueObj()); 195 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 196 EXPECT_NE(handle, nullptr); 197 EXPECT_TRUE(ApiHandle::isImmediate(handle)); 198 ApiHandleDict* dict = capiHandles(runtime_); 199 EXPECT_EQ(dict->at(*obj), nullptr); 200 ApiHandle::decref(handle); 201} 202 203TEST_F(ApiHandleTest, BuiltinImmediateFalseObjectReturnsImmediateApiHandle) { 204 HandleScope scope(thread_); 205 Object obj(&scope, Bool::falseObj()); 206 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 207 EXPECT_NE(handle, nullptr); 208 EXPECT_TRUE(ApiHandle::isImmediate(handle)); 209 ApiHandleDict* dict = capiHandles(runtime_); 210 EXPECT_EQ(dict->at(*obj), nullptr); 211 ApiHandle::decref(handle); 212} 213 214TEST_F(ApiHandleTest, 215 BuiltinImmediateNotImplementedObjectReturnsImmediateApiHandle) { 216 HandleScope scope(thread_); 217 Object obj(&scope, NotImplementedType::object()); 218 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 219 EXPECT_NE(handle, nullptr); 220 EXPECT_TRUE(ApiHandle::isImmediate(handle)); 221 ApiHandleDict* dict = capiHandles(runtime_); 222 EXPECT_EQ(dict->at(*obj), nullptr); 223 ApiHandle::decref(handle); 224} 225 226TEST_F(ApiHandleTest, BuiltinImmediateUnboundObjectReturnsImmediateApiHandle) { 227 HandleScope scope(thread_); 228 Object obj(&scope, Unbound::object()); 229 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 230 EXPECT_NE(handle, nullptr); 231 EXPECT_TRUE(ApiHandle::isImmediate(handle)); 232 ApiHandleDict* dict = capiHandles(runtime_); 233 EXPECT_EQ(dict->at(*obj), nullptr); 234 ApiHandle::decref(handle); 235} 236 237TEST_F(ApiHandleTest, ApiHandleReturnsBuiltinIntObject) { 238 HandleScope scope(thread_); 239 240 Object obj(&scope, runtime_->newInt(1)); 241 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 242 Object handle_obj(&scope, ApiHandle::asObject(handle)); 243 EXPECT_TRUE(isIntEqualsWord(*handle_obj, 1)); 244} 245 246TEST_F(ApiHandleTest, BuiltinObjectReturnsApiHandle) { 247 HandleScope scope(thread_); 248 249 ApiHandleDict* dict = capiHandles(runtime_); 250 Object obj(&scope, runtime_->newList()); 251 ASSERT_FALSE(dict->at(*obj) != nullptr); 252 253 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 254 EXPECT_NE(handle, nullptr); 255 256 EXPECT_TRUE(dict->at(*obj) != nullptr); 257} 258 259TEST_F(ApiHandleTest, BuiltinObjectReturnsSameApiHandle) { 260 HandleScope scope(thread_); 261 Object obj(&scope, runtime_->newList()); 262 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 263 ApiHandle* handle2 = ApiHandle::newReference(runtime_, *obj); 264 EXPECT_EQ(handle, handle2); 265} 266 267TEST_F(ApiHandleTest, ApiHandleReturnsBuiltinObject) { 268 HandleScope scope(thread_); 269 Object obj(&scope, runtime_->newList()); 270 ApiHandle* handle = ApiHandle::newReference(runtime_, *obj); 271 Object handle_obj(&scope, ApiHandle::asObject(handle)); 272 EXPECT_TRUE(handle_obj.isList()); 273} 274 275TEST_F(ApiHandleTest, ExtensionInstanceObjectReturnsPyObject) { 276 HandleScope scope(thread_); 277 278 // Create type 279 PyObject extension_type; 280 Type type(&scope, initializeExtensionType(&extension_type)); 281 Layout layout(&scope, type.instanceLayout()); 282 283 // Create instance 284 NativeProxy proxy(&scope, runtime_->newInstance(layout)); 285 PyObject pyobj = {0, 1}; 286 proxy.setNative(runtime_->newIntFromCPtr(&pyobj)); 287 288 PyObject* result = ApiHandle::newReference(runtime_, *proxy); 289 EXPECT_TRUE(result); 290 EXPECT_EQ(result, &pyobj); 291} 292 293TEST_F(ApiHandleTest, RuntimeInstanceObjectReturnsPyObject) { 294 HandleScope scope(thread_); 295 296 // Create instance 297 Layout layout(&scope, runtime_->layoutAt(LayoutId::kObject)); 298 Object instance(&scope, runtime_->newInstance(layout)); 299 PyObject* result = ApiHandle::newReference(runtime_, *instance); 300 ASSERT_NE(result, nullptr); 301 302 Object obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(result))); 303 EXPECT_EQ(*obj, *instance); 304} 305 306TEST_F(ApiHandleTest, 307 CheckFunctionResultNonNullptrWithoutPendingExceptionReturnsResult) { 308 RawObject value = SmallInt::fromWord(1234); 309 ApiHandle* handle = ApiHandle::newReference(runtime_, value); 310 RawObject result = ApiHandle::checkFunctionResult(thread_, handle); 311 EXPECT_EQ(result, value); 312} 313 314TEST_F(ApiHandleTest, 315 CheckFunctionResultNullptrWithPendingExceptionReturnsError) { 316 thread_->raiseBadArgument(); // TypeError 317 RawObject result = ApiHandle::checkFunctionResult(thread_, nullptr); 318 EXPECT_TRUE(result.isErrorException()); 319 EXPECT_TRUE(thread_->hasPendingException()); 320 EXPECT_TRUE(thread_->pendingExceptionMatches(LayoutId::kTypeError)); 321} 322 323TEST_F(ApiHandleTest, 324 CheckFunctionResultNullptrWithoutPendingExceptionRaisesSystemError) { 325 EXPECT_FALSE(thread_->hasPendingException()); 326 RawObject result = ApiHandle::checkFunctionResult(thread_, nullptr); 327 EXPECT_TRUE(result.isErrorException()); 328 EXPECT_TRUE(thread_->hasPendingException()); 329 EXPECT_TRUE(thread_->pendingExceptionMatches(LayoutId::kSystemError)); 330} 331 332TEST_F(ApiHandleTest, 333 CheckFunctionResultNonNullptrWithPendingExceptionRaisesSystemError) { 334 thread_->raiseBadArgument(); // TypeError 335 ApiHandle* handle = 336 ApiHandle::newReference(runtime_, SmallInt::fromWord(1234)); 337 RawObject result = ApiHandle::checkFunctionResult(thread_, handle); 338 EXPECT_TRUE(result.isErrorException()); 339 EXPECT_TRUE(thread_->hasPendingException()); 340 EXPECT_TRUE(thread_->pendingExceptionMatches(LayoutId::kSystemError)); 341} 342 343TEST_F(ApiHandleTest, Cache) { 344 HandleScope scope(thread_); 345 346 auto handle1 = ApiHandle::newReference(runtime_, runtime_->newList()); 347 EXPECT_EQ(ApiHandle::cache(runtime_, handle1), nullptr); 348 349 Str str(&scope, 350 runtime_->newStrFromCStr("this is too long for a RawSmallStr")); 351 auto handle2 = ApiHandle::newReference(runtime_, *str); 352 EXPECT_EQ(ApiHandle::cache(runtime_, handle2), nullptr); 353 354 void* buffer1 = std::malloc(16); 355 ApiHandle::setCache(runtime_, handle1, buffer1); 356 EXPECT_EQ(ApiHandle::cache(runtime_, handle1), buffer1); 357 EXPECT_EQ(ApiHandle::cache(runtime_, handle2), nullptr); 358 359 void* buffer2 = std::malloc(16); 360 ApiHandle::setCache(runtime_, handle2, buffer2); 361 EXPECT_EQ(ApiHandle::cache(runtime_, handle2), buffer2); 362 EXPECT_EQ(ApiHandle::cache(runtime_, handle1), buffer1); 363 364 ApiHandle::setCache(runtime_, handle1, buffer2); 365 ApiHandle::setCache(runtime_, handle2, buffer1); 366 EXPECT_EQ(ApiHandle::cache(runtime_, handle1), buffer2); 367 EXPECT_EQ(ApiHandle::cache(runtime_, handle2), buffer1); 368 369 Object key(&scope, ApiHandle::asObject(handle1)); 370 ApiHandle::disposeWithRuntime(runtime_, handle1); 371 ApiHandleDict* caches = capiCaches(runtime_); 372 EXPECT_FALSE(caches->at(*key) != nullptr); 373 EXPECT_EQ(ApiHandle::cache(runtime_, handle2), buffer1); 374} 375 376TEST_F(ApiHandleTest, VisitApiHandlesVisitsAllHandles) { 377 HandleScope scope(thread_); 378 379 Object obj0(&scope, runtime_->newDict()); 380 Object obj1(&scope, runtime_->newStrFromCStr("hello world")); 381 ApiHandle* handle0 = ApiHandle::newReference(runtime_, *obj0); 382 ApiHandle* handle1 = ApiHandle::borrowedReference(runtime_, *obj1); 383 384 struct Visitor : public HandleVisitor { 385 void visitHandle(void* handle, RawObject object) override { 386 if (object == obj0) { 387 EXPECT_EQ(obj0_handle, nullptr); 388 obj0_handle = handle; 389 } 390 if (object == obj1) { 391 EXPECT_EQ(obj1_handle, nullptr); 392 obj1_handle = handle; 393 } 394 } 395 396 RawObject obj0 = NoneType::object(); 397 RawObject obj1 = NoneType::object(); 398 void* obj0_handle = nullptr; 399 void* obj1_handle = nullptr; 400 }; 401 402 Visitor visitor; 403 visitor.obj0 = *obj0; 404 visitor.obj1 = *obj1; 405 visitApiHandles(runtime_, &visitor); 406 407 EXPECT_EQ(visitor.obj0_handle, handle0); 408 EXPECT_EQ(visitor.obj1_handle, handle1); 409 ApiHandle::decref(handle0); 410} 411 412TEST_F(CApiHandlesDeathTest, CleanupApiHandlesOnExit) { 413 HandleScope scope(thread_); 414 Object obj(&scope, runtime_->newStrFromCStr("hello")); 415 ApiHandle::newReference(runtime_, *obj); 416 ASSERT_EXIT(static_cast<void>(runFromCStr(runtime_, R"( 417import sys 418sys.exit() 419)")), 420 ::testing::ExitedWithCode(0), ""); 421} 422 423} // namespace testing 424} // namespace py