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