this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "Python.h"
3#include "gtest/gtest.h"
4#include "structmember.h"
5
6#include "capi-fixture.h"
7#include "capi-testing.h"
8
9namespace py {
10namespace testing {
11
12using TypeExtensionApiTest = ExtensionApi;
13using TypeExtensionApiDeathTest = ExtensionApi;
14
15// Common deallocation function for types with only primitive members
16static void deallocLeafObject(PyObject* self) {
17 PyTypeObject* type = Py_TYPE(self);
18 PyObject_Del(self);
19 Py_DECREF(type);
20}
21
22TEST_F(TypeExtensionApiTest, PyTypeCheckOnLong) {
23 PyObjectPtr pylong(PyLong_FromLong(10));
24 EXPECT_FALSE(PyType_Check(pylong));
25 EXPECT_FALSE(PyType_CheckExact(pylong));
26}
27
28TEST_F(TypeExtensionApiTest, PyTypeCheckOnType) {
29 PyObjectPtr pylong(PyLong_FromLong(10));
30 PyObjectPtr pylong_type(PyObject_Type(pylong));
31 EXPECT_TRUE(PyType_Check(pylong_type));
32 EXPECT_TRUE(PyType_CheckExact(pylong_type));
33}
34
35TEST_F(TypeExtensionApiTest,
36 PyTypeGenericNewWithTypeWithoutNativeDataReturnsPyObject) {
37 PyType_Slot slots[] = {
38 {Py_tp_new, reinterpret_cast<void*>(PyType_GenericNew)},
39 {0, nullptr},
40 };
41 static PyType_Spec spec;
42 spec = {
43 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
44 };
45 PyObjectPtr ext_type(PyType_FromSpec(&spec));
46 ASSERT_NE(ext_type, nullptr);
47 ASSERT_TRUE(PyType_CheckExact(ext_type));
48 EXPECT_EQ(PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new),
49 reinterpret_cast<void*>(PyType_GenericNew));
50 auto new_slot = reinterpret_cast<newfunc>(
51 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
52 PyObjectPtr args(PyTuple_New(0));
53 PyObjectPtr kwargs(PyDict_New());
54 PyObjectPtr result(new_slot(ext_type.asTypeObject(), args, kwargs));
55 ASSERT_NE(result, nullptr);
56 EXPECT_EQ(PyObject_IsInstance(result, ext_type), 1);
57
58 // Ensure that a managed subtype of the native type can be created
59 // via its __new__ function.
60 testing::moduleSet("__main__", "Bar", ext_type);
61 EXPECT_EQ(PyRun_SimpleString(R"(
62class SubBar(Bar):
63 pass
64
65s = SubBar()
66)"),
67 0);
68}
69
70TEST_F(TypeExtensionApiDeathTest, GetFlagsFromManagedTypePyro) {
71 PyRun_SimpleString(R"(class Foo: pass)");
72 PyObjectPtr foo_type(testing::mainModuleGet("Foo"));
73 ASSERT_TRUE(PyType_CheckExact(foo_type));
74 EXPECT_DEATH(
75 PyType_GetFlags(reinterpret_cast<PyTypeObject*>(foo_type.get())),
76 "unimplemented: GetFlags from types initialized through Python code");
77}
78
79TEST_F(TypeExtensionApiTest, GetFlagsFromExtensionTypeReturnsSetFlags) {
80 PyType_Slot slots[] = {
81 {0, nullptr},
82 };
83 static PyType_Spec spec;
84 spec = {
85 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
86 };
87 PyObjectPtr type(PyType_FromSpec(&spec));
88 ASSERT_NE(type, nullptr);
89 ASSERT_TRUE(PyType_CheckExact(type));
90
91 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(type.get())) &
92 Py_TPFLAGS_DEFAULT);
93 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(type.get())) &
94 Py_TPFLAGS_READY);
95 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(type.get())) &
96 Py_TPFLAGS_HEAPTYPE);
97}
98
99TEST_F(TypeExtensionApiTest, FromSpecCreatesRuntimeType) {
100 PyType_Slot slots[] = {
101 {0, nullptr},
102 };
103 static PyType_Spec spec;
104 spec = {
105 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
106 };
107 PyObjectPtr type(PyType_FromSpec(&spec));
108 ASSERT_NE(type, nullptr);
109 ASSERT_TRUE(PyType_CheckExact(type));
110
111 testing::moduleSet("__main__", "Empty", type);
112 PyRun_SimpleString("x = Empty");
113 PyObjectPtr result(testing::mainModuleGet("x"));
114 EXPECT_TRUE(PyType_CheckExact(result));
115 PyObjectPtr module(PyObject_GetAttrString(result, "__module__"));
116 EXPECT_TRUE(isUnicodeEqualsCStr(module, "foo"));
117 PyObjectPtr name(PyObject_GetAttrString(result, "__name__"));
118 EXPECT_TRUE(isUnicodeEqualsCStr(name, "Bar"));
119 PyObjectPtr qualname(PyObject_GetAttrString(result, "__qualname__"));
120 EXPECT_TRUE(isUnicodeEqualsCStr(qualname, "Bar"));
121}
122
123TEST_F(TypeExtensionApiTest, FromSpecWithInvalidSlotRaisesError) {
124 PyType_Slot slots[] = {
125 {-1, nullptr},
126 {0, nullptr},
127 };
128 static PyType_Spec spec;
129 spec = {
130 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
131 };
132 ASSERT_EQ(PyType_FromSpec(&spec), nullptr);
133 ASSERT_NE(PyErr_Occurred(), nullptr);
134 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_RuntimeError));
135 // TODO(eelizondo): Check that error matches with "invalid slot offset"
136}
137
138TEST_F(TypeExtensionApiTest,
139 FromSpecWithZeroBasicSizeAndItemSetsTpNewOfManagedTypePyro) {
140 PyType_Slot slots[] = {
141 {0, nullptr},
142 };
143 static PyType_Spec spec;
144 spec = {
145 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
146 };
147 PyObjectPtr ext_type(PyType_FromSpec(&spec));
148 ASSERT_NE(ext_type, nullptr);
149 ASSERT_TRUE(PyType_CheckExact(ext_type));
150
151 PyRun_SimpleString(R"(class D: pass)");
152 PyObjectPtr managed_type(mainModuleGet("D"));
153 ASSERT_NE(PyType_GetSlot(managed_type.asTypeObject(), Py_tp_new), nullptr);
154
155 // Instances of `ext_type` does not cause any memory leak.
156 auto new_slot = reinterpret_cast<newfunc>(
157 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
158 PyObjectPtr args(PyTuple_New(0));
159 PyObjectPtr kwargs(PyDict_New());
160 PyObjectPtr result(new_slot(ext_type.asTypeObject(), args, kwargs));
161 ASSERT_NE(result, nullptr);
162 EXPECT_EQ(PyObject_IsInstance(result, ext_type), 1);
163}
164
165TEST_F(TypeExtensionApiTest,
166 FromSpecWithNonZeroBasicSizeAndItemSetsCustomTpNewPyro) {
167 PyType_Slot slots[] = {
168 {0, nullptr},
169 };
170 struct BarState {
171 PyObject_HEAD
172 int foo;
173 };
174 static PyType_Spec spec;
175 spec = {
176 "foo.Bar", sizeof(BarState), 0, Py_TPFLAGS_DEFAULT, slots,
177 };
178 PyObjectPtr ext_type(PyType_FromSpec(&spec));
179 ASSERT_NE(ext_type, nullptr);
180 ASSERT_TRUE(PyType_CheckExact(ext_type));
181
182 PyRun_SimpleString(R"(class D: pass)");
183 PyObjectPtr managed_type(mainModuleGet("D"));
184 ASSERT_NE(PyType_GetSlot(managed_type.asTypeObject(), Py_tp_new), nullptr);
185 EXPECT_NE(PyType_GetSlot(managed_type.asTypeObject(), Py_tp_new),
186 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
187
188 // Instances of `ext_type` does not cause any memory leak.
189 auto new_slot = reinterpret_cast<newfunc>(
190 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
191 PyObjectPtr args(PyTuple_New(0));
192 PyObjectPtr kwargs(PyDict_New());
193 PyObjectPtr result(new_slot(ext_type.asTypeObject(), args, kwargs));
194 ASSERT_NE(result, nullptr);
195 EXPECT_EQ(PyObject_IsInstance(result, ext_type), 1);
196}
197
198TEST_F(TypeExtensionApiTest, CallExtensionTypeReturnsExtensionInstancePyro) {
199 struct BarObject {
200 PyObject_HEAD
201 int value;
202 };
203 newfunc new_func = [](PyTypeObject* type, PyObject*, PyObject*) {
204 void* slot = PyType_GetSlot(type, Py_tp_alloc);
205 return reinterpret_cast<allocfunc>(slot)(type, 0);
206 };
207 initproc init_func = [](PyObject* self, PyObject*, PyObject*) {
208 reinterpret_cast<BarObject*>(self)->value = 30;
209 return 0;
210 };
211 PyType_Slot slots[] = {
212 {Py_tp_alloc, reinterpret_cast<void*>(PyType_GenericAlloc)},
213 {Py_tp_new, reinterpret_cast<void*>(new_func)},
214 {Py_tp_init, reinterpret_cast<void*>(init_func)},
215 {Py_tp_dealloc, reinterpret_cast<void*>(deallocLeafObject)},
216 {0, nullptr},
217 };
218 static PyType_Spec spec;
219 spec = {
220 "foo.Bar", sizeof(BarObject), 0, Py_TPFLAGS_DEFAULT, slots,
221 };
222 PyObjectPtr type(PyType_FromSpec(&spec));
223 ASSERT_NE(type, nullptr);
224 ASSERT_TRUE(PyType_CheckExact(type));
225
226 testing::moduleSet("__main__", "Bar", type);
227 PyRun_SimpleString(R"(
228bar = Bar()
229)");
230
231 PyObjectPtr bar(testing::mainModuleGet("bar"));
232 ASSERT_NE(bar, nullptr);
233 BarObject* barobj = reinterpret_cast<BarObject*>(bar.get());
234 EXPECT_EQ(barobj->value, 30);
235}
236
237TEST_F(TypeExtensionApiTest, GenericAllocationReturnsMallocMemory) {
238 // These numbers determine the allocated size of the PyObject
239 // The values in this test are abitrary and are usally set with `sizeof(Foo)`
240 int basic_size = sizeof(PyObject) + 10;
241 int item_size = 5;
242 PyType_Slot slots[] = {
243 {Py_tp_dealloc, reinterpret_cast<void*>(deallocLeafObject)},
244 {0, nullptr},
245 };
246 static PyType_Spec spec;
247 spec = {
248 "foo.Bar", basic_size, item_size, Py_TPFLAGS_DEFAULT, slots,
249 };
250 PyObjectPtr type(PyType_FromSpec(&spec));
251 ASSERT_NE(type, nullptr);
252 ASSERT_TRUE(PyType_CheckExact(type));
253
254 PyObjectPtr result(PyType_GenericAlloc(
255 reinterpret_cast<PyTypeObject*>(type.get()), item_size));
256 ASSERT_NE(result, nullptr);
257 ASSERT_GE(Py_REFCNT(result), 1); // CPython
258 ASSERT_LE(Py_REFCNT(result), 2); // Pyro
259 EXPECT_EQ(Py_SIZE(result.get()), item_size);
260}
261
262TEST_F(TypeExtensionApiTest, GetSlotTpNewOnManagedTypeReturnsSlot) {
263 ASSERT_EQ(PyRun_SimpleString(R"(
264class Foo:
265 def __new__(ty, a, b, c, d):
266 obj = super().__new__(ty)
267 obj.args = (a, b, c, d)
268 return obj
269)"),
270 0);
271
272 PyObjectPtr foo(mainModuleGet("Foo"));
273 auto new_slot =
274 reinterpret_cast<newfunc>(PyType_GetSlot(foo.asTypeObject(), Py_tp_new));
275 ASSERT_NE(new_slot, nullptr);
276 PyObjectPtr one(PyLong_FromLong(1));
277 PyObjectPtr two(PyLong_FromLong(2));
278 PyObjectPtr cee(PyUnicode_FromString("cee"));
279 PyObjectPtr dee(PyUnicode_FromString("dee"));
280 PyObjectPtr args(PyTuple_Pack(2, one.get(), two.get()));
281 PyObjectPtr kwargs(PyDict_New());
282 PyDict_SetItemString(kwargs, "d", dee);
283 PyDict_SetItemString(kwargs, "c", cee);
284
285 PyObjectPtr result(new_slot(foo.asTypeObject(), args, kwargs));
286 ASSERT_NE(result, nullptr);
287 ASSERT_EQ(PyObject_IsInstance(result, foo), 1);
288 PyObjectPtr obj_args(PyObject_GetAttrString(result, "args"));
289 ASSERT_NE(obj_args, nullptr);
290 ASSERT_EQ(PyTuple_CheckExact(obj_args), 1);
291 ASSERT_EQ(PyTuple_Size(obj_args), 4);
292 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(obj_args, 0), 1));
293 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(obj_args, 1), 2));
294 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(obj_args, 2), "cee"));
295 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(obj_args, 3), "dee"));
296}
297
298TEST_F(TypeExtensionApiTest, IsSubtypeWithSameTypeReturnsTrue) {
299 PyObjectPtr pylong(PyLong_FromLong(10));
300 PyObjectPtr pylong_type(PyObject_Type(pylong));
301 EXPECT_TRUE(
302 PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(pylong_type.get()),
303 reinterpret_cast<PyTypeObject*>(pylong_type.get())));
304}
305
306TEST_F(TypeExtensionApiTest, IsSubtypeWithSubtypeReturnsTrue) {
307 EXPECT_EQ(PyRun_SimpleString("class MyFloat(float): pass"), 0);
308 PyObjectPtr pyfloat(PyFloat_FromDouble(1.23));
309 PyObjectPtr pyfloat_type(PyObject_Type(pyfloat));
310 PyObjectPtr myfloat_type(mainModuleGet("MyFloat"));
311 EXPECT_TRUE(
312 PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(myfloat_type.get()),
313 reinterpret_cast<PyTypeObject*>(pyfloat_type.get())));
314}
315
316TEST_F(TypeExtensionApiTest, IsSubtypeWithDifferentTypesReturnsFalse) {
317 PyObjectPtr pylong(PyLong_FromLong(10));
318 PyObjectPtr pylong_type(PyObject_Type(pylong));
319 PyObjectPtr pyuni(PyUnicode_FromString("string"));
320 PyObjectPtr pyuni_type(PyObject_Type(pyuni));
321 EXPECT_FALSE(
322 PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(pylong_type.get()),
323 reinterpret_cast<PyTypeObject*>(pyuni_type.get())));
324}
325
326TEST_F(TypeExtensionApiTest, PyTypeModifiedWithHeapTypeDoesNothing) {
327 PyRun_SimpleString(R"(
328class C:
329 pass
330)");
331 PyObjectPtr c(mainModuleGet("C"));
332 PyType_Modified(c.asTypeObject());
333}
334
335TEST_F(TypeExtensionApiTest, GetSlotFromBuiltinTypeRaisesSystemError) {
336 PyObjectPtr pylong(PyLong_FromLong(5));
337 PyObjectPtr pylong_type(PyObject_Type(pylong));
338 ASSERT_TRUE(
339 PyType_CheckExact(reinterpret_cast<PyTypeObject*>(pylong_type.get())));
340
341 EXPECT_EQ(PyType_GetSlot(reinterpret_cast<PyTypeObject*>(pylong_type.get()),
342 Py_tp_init),
343 nullptr);
344 ASSERT_NE(PyErr_Occurred(), nullptr);
345 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
346}
347
348TEST_F(TypeExtensionApiDeathTest,
349 GetSlotFromManagedTypeReturnsFunctionPointerPyro) {
350 PyRun_SimpleString(R"(
351class Foo:
352 def __init__(self):
353 pass
354 )");
355
356 PyObjectPtr foo_type(testing::mainModuleGet("Foo"));
357 ASSERT_TRUE(PyType_CheckExact(foo_type));
358 EXPECT_DEATH(PyType_GetSlot(reinterpret_cast<PyTypeObject*>(foo_type.get()),
359 Py_tp_init),
360 "Unsupported default slot");
361}
362
363TEST_F(TypeExtensionApiTest, GetUnsupportedSlotFromManagedTypeAbortsPyro) {
364 PyRun_SimpleString(R"(
365class Foo: pass
366 )");
367
368 PyObjectPtr foo_type(testing::mainModuleGet("Foo"));
369 ASSERT_TRUE(PyType_CheckExact(foo_type));
370 EXPECT_DEATH(
371 PyType_GetSlot(reinterpret_cast<PyTypeObject*>(foo_type.get()), Py_nb_or),
372 "Unsupported default slot");
373}
374
375TEST_F(TypeExtensionApiTest, GetSetDescriptorTypeMatchesPyTpGetSet) {
376 struct BarObject {
377 PyObject_HEAD
378 long attribute;
379 };
380
381 getter attribute_getter = [](PyObject* self, void*) {
382 return PyLong_FromLong(reinterpret_cast<BarObject*>(self)->attribute);
383 };
384
385 setter attribute_setter = [](PyObject* self, PyObject* value, void*) {
386 reinterpret_cast<BarObject*>(self)->attribute = PyLong_AsLong(value);
387 return 0;
388 };
389
390 static PyGetSetDef getsets[2];
391 getsets[0] = {"attribute", attribute_getter, attribute_setter};
392 getsets[1] = {nullptr};
393
394 static PyType_Slot slots[2];
395 slots[0] = {Py_tp_getset, reinterpret_cast<void*>(getsets)};
396 slots[1] = {0, nullptr};
397 static PyType_Spec spec;
398 spec = {
399 "__main__.Bar", sizeof(BarObject), 0, Py_TPFLAGS_DEFAULT, slots,
400 };
401 PyObjectPtr type(PyType_FromSpec(&spec));
402 ASSERT_EQ(PyType_CheckExact(type), 1);
403 ASSERT_EQ(moduleSet("__main__", "Bar", type), 0);
404 PyRun_SimpleString(R"(
405import types
406descrType = types.GetSetDescriptorType
407tpType = type(Bar.__dict__['attribute'])
408)");
409 PyObjectPtr descr_type(testing::mainModuleGet("descrType"));
410 PyObjectPtr tp_type(testing::mainModuleGet("tpType"));
411 ASSERT_EQ(descr_type, tp_type);
412}
413
414TEST_F(TypeExtensionApiTest, GetSlotFromNegativeSlotRaisesSystemError) {
415 PyRun_SimpleString(R"(
416class Foo: pass
417 )");
418
419 PyObjectPtr foo_type(testing::mainModuleGet("Foo"));
420 ASSERT_TRUE(PyType_CheckExact(foo_type));
421
422 EXPECT_EQ(PyType_GetSlot(reinterpret_cast<PyTypeObject*>(foo_type.get()), -1),
423 nullptr);
424
425 ASSERT_NE(PyErr_Occurred(), nullptr);
426 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
427}
428
429TEST_F(TypeExtensionApiTest, GetSlotFromLargerThanMaxSlotReturnsNull) {
430 PyRun_SimpleString(R"(
431class Foo: pass
432 )");
433
434 PyObjectPtr foo_type(testing::mainModuleGet("Foo"));
435 ASSERT_TRUE(PyType_CheckExact(foo_type));
436
437 EXPECT_EQ(
438 PyType_GetSlot(reinterpret_cast<PyTypeObject*>(foo_type.get()), 1000),
439 nullptr);
440 EXPECT_EQ(PyErr_Occurred(), nullptr);
441}
442
443TEST_F(TypeExtensionApiTest, GetSlotFromExtensionType) {
444 newfunc new_func = [](PyTypeObject* type, PyObject*, PyObject*) {
445 void* slot = PyType_GetSlot(type, Py_tp_alloc);
446 return reinterpret_cast<allocfunc>(slot)(type, 0);
447 };
448 initproc init_func = [](PyObject*, PyObject*, PyObject*) { return 0; };
449 binaryfunc add_func = [](PyObject*, PyObject*) { return PyLong_FromLong(7); };
450 PyType_Slot slots[] = {
451 {Py_tp_alloc, reinterpret_cast<void*>(PyType_GenericAlloc)},
452 {Py_tp_new, reinterpret_cast<void*>(new_func)},
453 {Py_tp_init, reinterpret_cast<void*>(init_func)},
454 {Py_nb_add, reinterpret_cast<void*>(add_func)},
455 {0, nullptr},
456 };
457 static PyType_Spec spec;
458 spec = {
459 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
460 };
461 PyObjectPtr type(PyType_FromSpec(&spec));
462 ASSERT_NE(type, nullptr);
463 ASSERT_TRUE(PyType_CheckExact(type));
464
465 PyTypeObject* typeobj = reinterpret_cast<PyTypeObject*>(type.get());
466 EXPECT_EQ(PyType_GetSlot(typeobj, Py_tp_alloc),
467 reinterpret_cast<void*>(PyType_GenericAlloc));
468 EXPECT_EQ(PyType_GetSlot(typeobj, Py_tp_new),
469 reinterpret_cast<void*>(new_func));
470 EXPECT_EQ(PyType_GetSlot(typeobj, Py_tp_init),
471 reinterpret_cast<void*>(init_func));
472 EXPECT_EQ(PyType_GetSlot(typeobj, Py_nb_add),
473 reinterpret_cast<void*>(add_func));
474 EXPECT_EQ(PyErr_Occurred(), nullptr);
475}
476
477TEST_F(TypeExtensionApiTest, DunderBasicsizeWithExtensionTypeReturnsBasicsize) {
478 PyType_Slot slots[] = {
479 {0, nullptr},
480 };
481 int size = sizeof(PyObject) + 13;
482 static PyType_Spec spec;
483 spec = {
484 "foo.Bar", size, 0, Py_TPFLAGS_DEFAULT, slots,
485 };
486 PyObjectPtr type(PyType_FromSpec(&spec));
487 ASSERT_NE(type, nullptr);
488 PyObjectPtr basicsize(PyObject_GetAttrString(type, "__basicsize__"));
489 ASSERT_NE(basicsize, nullptr);
490 EXPECT_TRUE(isLongEqualsLong(basicsize, size));
491}
492
493TEST_F(TypeExtensionApiTest,
494 DunderBasicsizeExtensionTypeWithZeroSizeReturnsBasicsize) {
495 PyType_Slot slots[] = {
496 {0, nullptr},
497 };
498 static PyType_Spec spec;
499 spec = {
500 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
501 };
502 PyObjectPtr type(PyType_FromSpec(&spec));
503 ASSERT_NE(type, nullptr);
504 PyObjectPtr basicsize(PyObject_GetAttrString(type, "__basicsize__"));
505 ASSERT_NE(basicsize, nullptr);
506 EXPECT_TRUE(isLongEqualsLong(basicsize, sizeof(PyObject)));
507}
508
509TEST_F(TypeExtensionApiTest,
510 DunderBasicsizeExtensionTypeWithHeadSizeReturnsBasicsize) {
511 PyType_Slot slots[] = {
512 {0, nullptr},
513 };
514 static PyType_Spec spec;
515 spec = {
516 "foo.Bar", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT, slots,
517 };
518 PyObjectPtr type(PyType_FromSpec(&spec));
519 ASSERT_NE(type, nullptr);
520 PyObjectPtr basicsize(PyObject_GetAttrString(type, "__basicsize__"));
521 ASSERT_NE(basicsize, nullptr);
522 EXPECT_TRUE(isLongEqualsLong(basicsize, sizeof(PyObject)));
523}
524
525TEST_F(TypeExtensionApiTest,
526 MembersWithoutDunderDictoffsetReturnsTypeWithoutDunderDict) {
527 PyType_Slot slots[] = {
528 {0, nullptr},
529 };
530 static PyType_Spec spec;
531 spec = {"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots};
532 PyObjectPtr type(PyType_FromSpec(&spec));
533 ASSERT_NE(type, nullptr);
534
535 // Cannot set arbitrary attributes on instances.
536 PyObjectPtr instance(PyObject_CallObject(type, nullptr));
537 ASSERT_NE(instance, nullptr);
538 PyObjectPtr value(PyUnicode_FromString("world"));
539 EXPECT_EQ(PyObject_SetAttrString(instance, "hello", value), -1);
540 ASSERT_NE(PyErr_Occurred(), nullptr);
541 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_AttributeError));
542 PyErr_Clear();
543
544 // Has no `__dict__`.
545 EXPECT_EQ(PyObject_GetAttrString(instance, "__dict__"), nullptr);
546 PyErr_Clear();
547}
548
549TEST_F(TypeExtensionApiTest,
550 MembersWithDunderDictoffsetReturnsTypeWithDunderDict) {
551 struct BarObject {
552 PyObject_HEAD
553 PyObject* dict;
554 };
555 static PyMemberDef members[2];
556 members[0] = {"__dictoffset__", T_PYSSIZET, offsetof(BarObject, dict),
557 READONLY};
558 members[1] = {nullptr};
559
560 PyType_Slot slots[] = {
561 {Py_tp_members, members},
562 {0, nullptr},
563 };
564 static PyType_Spec spec;
565 spec = {"foo.Bar", 0, sizeof(BarObject), Py_TPFLAGS_DEFAULT, slots};
566 PyObjectPtr type(PyType_FromSpec(&spec));
567 ASSERT_NE(type, nullptr);
568
569 // Can set arbitrary attributes on instances.
570 PyObjectPtr instance(PyObject_CallObject(type, nullptr));
571 ASSERT_NE(instance, nullptr);
572 PyObjectPtr value(PyUnicode_FromString("world"));
573 EXPECT_EQ(PyObject_SetAttrString(instance, "hello", value), 0);
574 PyObjectPtr item(PyObject_GetAttrString(instance, "hello"));
575 EXPECT_TRUE(isUnicodeEqualsCStr(item, "world"));
576}
577
578TEST_F(TypeExtensionApiTest, MemberDescriptorTypeMatchesPyTpMembers) {
579 struct BarObject {
580 PyObject_HEAD
581 int value;
582 };
583
584 static PyMemberDef members[2];
585 members[0] = {"value", T_INT, offsetof(BarObject, value)};
586 members[1] = {nullptr};
587
588 static const PyType_Slot slots[] = {
589 {Py_tp_members, const_cast<PyMemberDef*>(members)},
590 {0, nullptr},
591 };
592 static PyType_Spec spec = {
593 "__main__.Bar",
594 sizeof(BarObject),
595 0,
596 Py_TPFLAGS_DEFAULT,
597 const_cast<PyType_Slot*>(slots),
598 };
599 PyObjectPtr type(PyType_FromSpec(&spec));
600 testing::moduleSet("__main__", "Bar", type);
601 PyRun_SimpleString(R"(
602import types
603descrType = types.MemberDescriptorType
604tpType = type(Bar.__dict__['value'])
605)");
606 PyObjectPtr descr_type(testing::mainModuleGet("descrType"));
607 PyObjectPtr tp_type(testing::mainModuleGet("tpType"));
608 ASSERT_EQ(descr_type, tp_type);
609}
610
611// METH_NOARGS and CALL_FUNCTION
612
613TEST_F(TypeExtensionApiTest, MethodsMethNoargsPosCall) {
614 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(1234); };
615 static PyMethodDef methods[] = {{"noargs", meth, METH_NOARGS}, {nullptr}};
616 PyType_Slot slots[] = {
617 {Py_tp_methods, methods},
618 {0, nullptr},
619 };
620 static PyType_Spec spec;
621 spec = {
622 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
623 };
624 PyObjectPtr type(PyType_FromSpec(&spec));
625 ASSERT_NE(type, nullptr);
626 testing::moduleSet("__main__", "C", type);
627 PyRun_SimpleString(R"(
628result = C().noargs()
629)");
630 PyObjectPtr result(testing::mainModuleGet("result"));
631 ASSERT_NE(result, nullptr);
632 EXPECT_EQ(PyLong_AsLong(result), 1234);
633}
634
635// METH_NOARGS | METH_CLASS | METH_STATIC and CALL_FUNCTION
636
637TEST_F(TypeExtensionApiTest, MethodsClassAndStaticRaisesValueError) {
638 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(1234); };
639 static PyMethodDef methods[] = {
640 {"noargs", meth, METH_NOARGS | METH_CLASS | METH_STATIC}, {nullptr}};
641 PyType_Slot slots[] = {
642 {Py_tp_methods, methods},
643 {0, nullptr},
644 };
645 static PyType_Spec spec;
646 spec = {
647 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
648 };
649 EXPECT_EQ(PyType_FromSpec(&spec), nullptr);
650 ASSERT_NE(PyErr_Occurred(), nullptr);
651 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ValueError));
652}
653
654TEST_F(TypeExtensionApiTest,
655 MethodsWithTypeSlotNameCoExistGetsResolvedForFunctionCall) {
656 newfunc new_func = [](PyTypeObject*, PyObject*, PyObject*) {
657 return PyLong_FromLong(100);
658 };
659 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(200); };
660 static PyMethodDef methods[] = {
661 {"__new__", meth, METH_NOARGS | METH_STATIC | METH_COEXIST}, {nullptr}};
662 PyType_Slot slots[] = {
663 {Py_tp_new, reinterpret_cast<void*>(new_func)},
664 {Py_tp_methods, methods},
665 {0, nullptr},
666 };
667 static PyType_Spec spec;
668 spec = {
669 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
670 };
671 PyObjectPtr type(PyType_FromSpec(&spec));
672 ASSERT_EQ(PyErr_Occurred(), nullptr);
673 EXPECT_EQ(
674 reinterpret_cast<newfunc>(PyType_GetSlot(type.asTypeObject(), Py_tp_new)),
675 new_func);
676 testing::moduleSet("__main__", "C", type);
677 PyRun_SimpleString(R"(
678result = C.__new__()
679)");
680 PyObjectPtr result(testing::moduleGet("__main__", "result"));
681 ASSERT_NE(result, nullptr);
682 EXPECT_EQ(PyLong_AsLong(result), 200);
683}
684
685TEST_F(TypeExtensionApiTest, MethodsWithTypeSlotNameClassAndStaticGetsIgnored) {
686 newfunc new_func = [](PyTypeObject*, PyObject*, PyObject*) {
687 return PyLong_FromLong(100);
688 };
689 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(200); };
690 static PyMethodDef methods[] = {{"__new__", meth, METH_NOARGS | METH_STATIC},
691 {nullptr}};
692 PyType_Slot slots[] = {
693 {Py_tp_new, reinterpret_cast<void*>(new_func)},
694 {Py_tp_methods, methods},
695 {0, nullptr},
696 };
697 static PyType_Spec spec;
698 spec = {
699 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
700 };
701 PyObjectPtr type(PyType_FromSpec(&spec));
702 ASSERT_EQ(PyErr_Occurred(), nullptr);
703 EXPECT_EQ(
704 reinterpret_cast<newfunc>(PyType_GetSlot(type.asTypeObject(), Py_tp_new)),
705 new_func);
706 testing::moduleSet("__main__", "C", type);
707 PyRun_SimpleString(R"(
708result = C.__new__(C)
709)");
710 PyObjectPtr result(testing::moduleGet("__main__", "result"));
711 ASSERT_NE(result, nullptr);
712 EXPECT_EQ(PyLong_AsLong(result), 100);
713}
714
715// METH_NOARGS and CALL_FUNCTION_EX
716
717TEST_F(TypeExtensionApiTest, MethodsMethNoargsExCall) {
718 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(1234); };
719 static PyMethodDef methods[] = {{"noargs", meth, METH_NOARGS}, {nullptr}};
720 PyType_Slot slots[] = {
721 {Py_tp_methods, methods},
722 {0, nullptr},
723 };
724 static PyType_Spec spec;
725 spec = {
726 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
727 };
728 PyObjectPtr type(PyType_FromSpec(&spec));
729 ASSERT_NE(type, nullptr);
730 testing::moduleSet("__main__", "C", type);
731 PyRun_SimpleString(R"(
732result = C().noargs(*[])
733)");
734 PyObjectPtr result(testing::mainModuleGet("result"));
735 ASSERT_NE(result, nullptr);
736 EXPECT_EQ(PyLong_AsLong(result), 1234);
737}
738
739// METH_NOARGS and CALL_FUNCTION_EX with VARKEYWORDS
740
741TEST_F(TypeExtensionApiTest, MethodsMethNoargsExNoKwargsCall) {
742 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(1234); };
743 static PyMethodDef methods[] = {{"noargs", meth, METH_NOARGS}, {nullptr}};
744 PyType_Slot slots[] = {
745 {Py_tp_methods, methods},
746 {0, nullptr},
747 };
748 static PyType_Spec spec;
749 spec = {
750 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
751 };
752 PyObjectPtr type(PyType_FromSpec(&spec));
753 ASSERT_NE(type, nullptr);
754 testing::moduleSet("__main__", "C", type);
755 PyRun_SimpleString(R"(
756result = C().noargs(*[],**{})
757)");
758 PyObjectPtr result(testing::mainModuleGet("result"));
759 ASSERT_NE(result, nullptr);
760 EXPECT_EQ(PyLong_AsLong(result), 1234);
761}
762
763TEST_F(TypeExtensionApiTest, MethodsMethNoargsExHasKwargsRaisesTypeError) {
764 binaryfunc meth = [](PyObject*, PyObject*) { return PyLong_FromLong(1234); };
765 static PyMethodDef methods[] = {{"noargs", meth, METH_NOARGS}, {nullptr}};
766 PyType_Slot slots[] = {
767 {Py_tp_methods, methods},
768 {0, nullptr},
769 };
770 static PyType_Spec spec;
771 spec = {
772 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
773 };
774 PyObjectPtr type(PyType_FromSpec(&spec));
775 ASSERT_NE(type, nullptr);
776 testing::moduleSet("__main__", "C", type);
777 PyRun_SimpleString(R"(
778self = C()
779result = False
780try:
781 self.noargs(*[],**{'foo': 'bar'})
782except:
783 result = True
784)");
785 PyObjectPtr result(testing::mainModuleGet("result"));
786 ASSERT_EQ(result, Py_True);
787}
788
789// METH_O and CALL_FUNCTION
790
791TEST_F(TypeExtensionApiTest, MethodsMethOneArgPosCall) {
792 binaryfunc meth = [](PyObject* self, PyObject* arg) {
793 return PyTuple_Pack(2, self, arg);
794 };
795 static PyMethodDef methods[] = {{"onearg", meth, METH_O}, {nullptr}};
796 PyType_Slot slots[] = {
797 {Py_tp_methods, methods},
798 {0, nullptr},
799 };
800 static PyType_Spec spec;
801 spec = {
802 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
803 };
804 PyObjectPtr type(PyType_FromSpec(&spec));
805 ASSERT_NE(type, nullptr);
806 testing::moduleSet("__main__", "C", type);
807 PyRun_SimpleString(R"(
808self = C()
809result = self.onearg(1234)
810)");
811 PyObjectPtr self(testing::mainModuleGet("self"));
812 PyObjectPtr result(testing::mainModuleGet("result"));
813 ASSERT_NE(result, nullptr);
814 ASSERT_EQ(PyTuple_CheckExact(result), 1);
815 ASSERT_EQ(PyTuple_Size(result), 2);
816 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
817 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(result, 1)), 1234);
818}
819
820TEST_F(TypeExtensionApiTest, MethodsMethOneArgNoArgsRaisesTypeError) {
821 binaryfunc meth = [](PyObject*, PyObject*) {
822 ADD_FAILURE(); // unreachable
823 return Py_None;
824 };
825 static PyMethodDef methods[] = {{"onearg", meth, METH_O}, {nullptr}};
826 PyType_Slot slots[] = {
827 {Py_tp_methods, methods},
828 {0, nullptr},
829 };
830 static PyType_Spec spec;
831 spec = {
832 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
833 };
834 PyObjectPtr type(PyType_FromSpec(&spec));
835 ASSERT_NE(type, nullptr);
836 testing::moduleSet("__main__", "C", type);
837 PyRun_SimpleString(R"(
838result = False
839self = C()
840try:
841 self.onearg()
842except TypeError:
843 result = True
844)");
845 PyObjectPtr result(testing::mainModuleGet("result"));
846 ASSERT_NE(result, nullptr);
847 EXPECT_EQ(result, Py_True);
848}
849
850// METH_O | METH_CLASS and CALL_FUNCTION
851
852TEST_F(TypeExtensionApiTest, MethodsMethOneArgClassPosCallOnClass) {
853 binaryfunc meth = [](PyObject* cls, PyObject* arg) {
854 return PyTuple_Pack(2, cls, arg);
855 };
856 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_CLASS},
857 {nullptr}};
858 PyType_Slot slots[] = {
859 {Py_tp_methods, methods},
860 {0, nullptr},
861 };
862 static PyType_Spec spec = {
863 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
864 };
865 PyObjectPtr type(PyType_FromSpec(&spec));
866 ASSERT_NE(type, nullptr);
867 testing::moduleSet("__main__", "C", type);
868 PyRun_SimpleString("result = C.onearg(1234)");
869 PyObjectPtr result(testing::mainModuleGet("result"));
870 ASSERT_NE(result, nullptr);
871 ASSERT_EQ(PyTuple_CheckExact(result), 1);
872 ASSERT_EQ(PyTuple_Size(result), 2);
873 EXPECT_EQ(PyTuple_GetItem(result, 0), type);
874 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
875}
876
877TEST_F(TypeExtensionApiTest, MethodsMethOneArgClassPosCallOnInstance) {
878 binaryfunc meth = [](PyObject* cls, PyObject* arg) {
879 return PyTuple_Pack(2, cls, arg);
880 };
881 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_CLASS},
882 {nullptr}};
883 PyType_Slot slots[] = {
884 {Py_tp_methods, methods},
885 {0, nullptr},
886 };
887 static PyType_Spec spec = {
888 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
889 };
890 PyObjectPtr type(PyType_FromSpec(&spec));
891 ASSERT_NE(type, nullptr);
892 testing::moduleSet("__main__", "C", type);
893 PyRun_SimpleString("result = C().onearg(1234)");
894 PyObjectPtr result(testing::mainModuleGet("result"));
895 ASSERT_NE(result, nullptr);
896 ASSERT_EQ(PyTuple_CheckExact(result), 1);
897 ASSERT_EQ(PyTuple_Size(result), 2);
898 EXPECT_EQ(PyTuple_GetItem(result, 0), type);
899 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
900}
901
902TEST_F(TypeExtensionApiTest, MethodsMethOneArgClassPosCallOnSubclass) {
903 binaryfunc meth = [](PyObject* cls, PyObject* arg) {
904 return PyTuple_Pack(2, cls, arg);
905 };
906 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_CLASS},
907 {nullptr}};
908 PyType_Slot slots[] = {
909 {Py_tp_methods, methods},
910 {0, nullptr},
911 };
912 static PyType_Spec spec = {
913 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
914 };
915 PyObjectPtr type(PyType_FromSpec(&spec));
916 ASSERT_NE(type, nullptr);
917 testing::moduleSet("__main__", "C", type);
918 PyRun_SimpleString(R"(
919class D(C):
920 pass
921result0 = D.onearg(1234)
922result1 = D().onearg(5678)
923)");
924 PyObjectPtr d(testing::mainModuleGet("D"));
925 PyObjectPtr result0(testing::mainModuleGet("result0"));
926 ASSERT_NE(result0, nullptr);
927 ASSERT_EQ(PyTuple_CheckExact(result0), 1);
928 ASSERT_EQ(PyTuple_Size(result0), 2);
929 EXPECT_EQ(PyTuple_GetItem(result0, 0), d);
930 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result0, 1), 1234));
931 PyObjectPtr result1(testing::mainModuleGet("result1"));
932 ASSERT_NE(result1, nullptr);
933 ASSERT_EQ(PyTuple_CheckExact(result1), 1);
934 ASSERT_EQ(PyTuple_Size(result1), 2);
935 EXPECT_EQ(PyTuple_GetItem(result1, 0), d);
936 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result1, 1), 5678));
937}
938
939// METH_O | METH_STATIC
940
941TEST_F(TypeExtensionApiTest, MethodsMethOneArgStaticCalledOnClass) {
942 binaryfunc meth = [](PyObject* self, PyObject* arg) {
943 EXPECT_EQ(self, nullptr);
944 Py_INCREF(arg);
945 return arg;
946 };
947 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_STATIC},
948 {nullptr}};
949 PyType_Slot slots[] = {
950 {Py_tp_methods, methods},
951 {0, nullptr},
952 };
953 static PyType_Spec spec = {
954 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
955 };
956 PyObjectPtr type(PyType_FromSpec(&spec));
957 ASSERT_NE(type, nullptr);
958 testing::moduleSet("__main__", "C", type);
959 PyRun_SimpleString("result = C.onearg(1234)");
960 PyObjectPtr result(testing::mainModuleGet("result"));
961 EXPECT_TRUE(isLongEqualsLong(result, 1234));
962}
963
964TEST_F(TypeExtensionApiTest, MethodsMethOneArgStaticCalledOnInstance) {
965 binaryfunc meth = [](PyObject* self, PyObject* arg) {
966 EXPECT_EQ(self, nullptr);
967 Py_INCREF(arg);
968 return arg;
969 };
970 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_STATIC},
971 {nullptr}};
972 PyType_Slot slots[] = {
973 {Py_tp_methods, methods},
974 {0, nullptr},
975 };
976 static PyType_Spec spec = {
977 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
978 };
979 PyObjectPtr type(PyType_FromSpec(&spec));
980 ASSERT_NE(type, nullptr);
981 testing::moduleSet("__main__", "C", type);
982 PyRun_SimpleString("result = C().onearg(1234)");
983 PyObjectPtr result(testing::mainModuleGet("result"));
984 EXPECT_TRUE(isLongEqualsLong(result, 1234));
985}
986
987TEST_F(TypeExtensionApiTest, MethodsMethOneArgStaticCalledOnSubclass) {
988 binaryfunc meth = [](PyObject* self, PyObject* arg) {
989 EXPECT_EQ(self, nullptr);
990 Py_INCREF(arg);
991 return arg;
992 };
993 static PyMethodDef methods[] = {{"onearg", meth, METH_O | METH_STATIC},
994 {nullptr}};
995 PyType_Slot slots[] = {
996 {Py_tp_methods, methods},
997 {0, nullptr},
998 };
999 static PyType_Spec spec = {
1000 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
1001 };
1002 PyObjectPtr type(PyType_FromSpec(&spec));
1003 ASSERT_NE(type, nullptr);
1004 testing::moduleSet("__main__", "C", type);
1005 PyRun_SimpleString(R"(
1006class D(C):
1007 pass
1008result0 = D.onearg(1234)
1009result1 = D().onearg(5678)
1010)");
1011 PyObjectPtr result0(testing::mainModuleGet("result0"));
1012 EXPECT_TRUE(isLongEqualsLong(result0, 1234));
1013 PyObjectPtr result1(testing::mainModuleGet("result1"));
1014 EXPECT_TRUE(isLongEqualsLong(result1, 5678));
1015}
1016
1017// METH_O and CALL_FUNCTION_KW
1018
1019TEST_F(TypeExtensionApiTest, MethodsMethOneArgKwCall) {
1020 binaryfunc meth = [](PyObject*, PyObject*) {
1021 ADD_FAILURE(); // unreachable
1022 return Py_None;
1023 };
1024 static PyMethodDef methods[] = {{"onearg", meth, METH_O}, {nullptr}};
1025 PyType_Slot slots[] = {
1026 {Py_tp_methods, methods},
1027 {0, nullptr},
1028 };
1029 static PyType_Spec spec;
1030 spec = {
1031 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1032 };
1033 PyObjectPtr type(PyType_FromSpec(&spec));
1034 ASSERT_NE(type, nullptr);
1035 testing::moduleSet("__main__", "C", type);
1036 ASSERT_NO_FATAL_FAILURE(PyRun_SimpleString(R"(
1037try:
1038 obj = C().onearg(foo=1234)
1039 result = False
1040except TypeError:
1041 result = True
1042)"));
1043 PyObjectPtr result(testing::mainModuleGet("result"));
1044 EXPECT_EQ(result, Py_True);
1045}
1046
1047// METH_O and CALL_FUNCTION_EX
1048
1049TEST_F(TypeExtensionApiTest, MethodsMethOneArgExCall) {
1050 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1051 return PyTuple_Pack(2, self, arg);
1052 };
1053 static PyMethodDef methods[] = {{"onearg", meth, METH_O}, {nullptr}};
1054 PyType_Slot slots[] = {
1055 {Py_tp_methods, methods},
1056 {0, nullptr},
1057 };
1058 static PyType_Spec spec;
1059 spec = {
1060 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1061 };
1062 PyObjectPtr type(PyType_FromSpec(&spec));
1063 ASSERT_NE(type, nullptr);
1064 testing::moduleSet("__main__", "C", type);
1065 PyRun_SimpleString(R"(
1066obj = C()
1067result = obj.onearg(*[1234])
1068)");
1069 PyObjectPtr obj(testing::mainModuleGet("obj"));
1070 PyObjectPtr result(testing::mainModuleGet("result"));
1071 ASSERT_NE(result, nullptr);
1072 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1073 ASSERT_EQ(PyTuple_Size(result), 2);
1074 EXPECT_EQ(PyTuple_GetItem(result, 0), obj);
1075 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(result, 1)), 1234);
1076}
1077
1078// METH_VARARGS and CALL_FUNCTION
1079
1080TEST_F(TypeExtensionApiTest, MethodsVarargsArgPosCall) {
1081 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1082 return PyTuple_Pack(2, self, arg);
1083 };
1084 static PyMethodDef methods[] = {{"varargs", meth, METH_VARARGS}, {nullptr}};
1085 PyType_Slot slots[] = {
1086 {Py_tp_methods, methods},
1087 {0, nullptr},
1088 };
1089 static PyType_Spec spec;
1090 spec = {
1091 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1092 };
1093 PyObjectPtr type(PyType_FromSpec(&spec));
1094 ASSERT_NE(type, nullptr);
1095 testing::moduleSet("__main__", "C", type);
1096 PyRun_SimpleString(R"(
1097self = C()
1098result = self.varargs(1234)
1099)");
1100 PyObjectPtr self(testing::mainModuleGet("self"));
1101 PyObjectPtr result(testing::mainModuleGet("result"));
1102 ASSERT_NE(result, nullptr);
1103 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1104 ASSERT_EQ(PyTuple_Size(result), 2);
1105 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1106 PyObject* args = PyTuple_GetItem(result, 1);
1107 EXPECT_TRUE(PyTuple_CheckExact(args));
1108 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1109}
1110
1111TEST_F(TypeExtensionApiTest, MethodsVarargsArgPosNoArgsCall) {
1112 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1113 return PyTuple_Pack(2, self, arg);
1114 };
1115 static PyMethodDef methods[] = {{"varargs", meth, METH_VARARGS}, {nullptr}};
1116 PyType_Slot slots[] = {
1117 {Py_tp_methods, methods},
1118 {0, nullptr},
1119 };
1120 static PyType_Spec spec;
1121 spec = {
1122 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1123 };
1124 PyObjectPtr type(PyType_FromSpec(&spec));
1125 ASSERT_NE(type, nullptr);
1126 testing::moduleSet("__main__", "C", type);
1127 PyRun_SimpleString(R"(
1128self = C()
1129result = self.varargs()
1130)");
1131 PyObjectPtr self(testing::mainModuleGet("self"));
1132 PyObjectPtr result(testing::mainModuleGet("result"));
1133 ASSERT_NE(result, nullptr);
1134 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1135 ASSERT_EQ(PyTuple_Size(result), 2);
1136 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1137 PyObject* args = PyTuple_GetItem(result, 1);
1138 EXPECT_TRUE(PyTuple_CheckExact(args));
1139 EXPECT_EQ(PyTuple_Size(args), 0);
1140}
1141
1142// METH_VARARGS and CALL_FUNCTION_KW
1143
1144TEST_F(TypeExtensionApiTest, MethodsVarargsArgKwCall) {
1145 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1146 return PyTuple_Pack(2, self, arg);
1147 };
1148 static PyMethodDef methods[] = {{"varargs", meth, METH_VARARGS}, {nullptr}};
1149 PyType_Slot slots[] = {
1150 {Py_tp_methods, methods},
1151 {0, nullptr},
1152 };
1153 static PyType_Spec spec;
1154 spec = {
1155 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1156 };
1157 PyObjectPtr type(PyType_FromSpec(&spec));
1158 ASSERT_NE(type, nullptr);
1159 testing::moduleSet("__main__", "C", type);
1160 PyRun_SimpleString(R"(
1161try:
1162 obj = C().varargs(foo=1234)
1163 result = False
1164except TypeError:
1165 result = True
1166)");
1167 PyObjectPtr result(testing::mainModuleGet("result"));
1168 EXPECT_EQ(result, Py_True);
1169}
1170
1171// METH_VARARGS and CALL_FUNCTION_EX
1172
1173TEST_F(TypeExtensionApiTest, MethodsVarargsArgExCall) {
1174 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1175 return PyTuple_Pack(2, self, arg);
1176 };
1177 static PyMethodDef methods[] = {{"varargs", meth, METH_VARARGS}, {nullptr}};
1178 PyType_Slot slots[] = {
1179 {Py_tp_methods, methods},
1180 {0, nullptr},
1181 };
1182 static PyType_Spec spec;
1183 spec = {
1184 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1185 };
1186 PyObjectPtr type(PyType_FromSpec(&spec));
1187 ASSERT_NE(type, nullptr);
1188 testing::moduleSet("__main__", "C", type);
1189 PyRun_SimpleString(R"(
1190self = C()
1191result = self.varargs(*[1234])
1192)");
1193 PyObjectPtr self(testing::mainModuleGet("self"));
1194 PyObjectPtr result(testing::mainModuleGet("result"));
1195 ASSERT_NE(result, nullptr);
1196 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1197 ASSERT_EQ(PyTuple_Size(result), 2);
1198 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1199 PyObject* args = PyTuple_GetItem(result, 1);
1200 EXPECT_TRUE(PyTuple_CheckExact(args));
1201 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1202}
1203
1204TEST_F(TypeExtensionApiTest, MethodsVarargsArgExHasEmptyKwargsCall) {
1205 binaryfunc meth = [](PyObject* self, PyObject* arg) {
1206 return PyTuple_Pack(2, self, arg);
1207 };
1208 static PyMethodDef methods[] = {{"varargs", meth, METH_VARARGS}, {nullptr}};
1209 PyType_Slot slots[] = {
1210 {Py_tp_methods, methods},
1211 {0, nullptr},
1212 };
1213 static PyType_Spec spec;
1214 spec = {
1215 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1216 };
1217 PyObjectPtr type(PyType_FromSpec(&spec));
1218 ASSERT_NE(type, nullptr);
1219 testing::moduleSet("__main__", "C", type);
1220 PyRun_SimpleString(R"(
1221self = C()
1222result = self.varargs(*[1234], **{})
1223)");
1224 PyObjectPtr self(testing::mainModuleGet("self"));
1225 PyObjectPtr result(testing::mainModuleGet("result"));
1226 ASSERT_NE(result, nullptr);
1227 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1228 ASSERT_EQ(PyTuple_Size(result), 2);
1229 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1230 PyObject* args = PyTuple_GetItem(result, 1);
1231 EXPECT_TRUE(PyTuple_CheckExact(args));
1232 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1233}
1234
1235// METH_KEYWORDS and CALL_FUNCTION
1236
1237TEST_F(TypeExtensionApiTest, MethodsMethKeywordsPosCall) {
1238 ternaryfunc meth = [](PyObject* self, PyObject* args, PyObject* kwargs) {
1239 if (kwargs == nullptr) return PyTuple_Pack(2, self, args);
1240 return PyTuple_Pack(3, self, args, kwargs);
1241 };
1242 static PyMethodDef methods[] = {
1243 {"keywords", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
1244 METH_VARARGS | METH_KEYWORDS},
1245 {nullptr}};
1246 PyType_Slot slots[] = {
1247 {Py_tp_methods, methods},
1248 {0, nullptr},
1249 };
1250
1251 static PyType_Spec spec;
1252 spec = {
1253 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1254 };
1255
1256 PyObjectPtr type(PyType_FromSpec(&spec));
1257 ASSERT_NE(type, nullptr);
1258 testing::moduleSet("__main__", "C", type);
1259 PyRun_SimpleString(R"(
1260self = C()
1261result = self.keywords(1234)
1262)");
1263
1264 PyObjectPtr self(testing::mainModuleGet("self"));
1265 PyObjectPtr result(testing::mainModuleGet("result"));
1266 ASSERT_NE(result, nullptr);
1267 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1268 ASSERT_EQ(PyTuple_Size(result), 2);
1269 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1270
1271 PyObject* args = PyTuple_GetItem(result, 1);
1272 EXPECT_TRUE(PyTuple_CheckExact(args));
1273 EXPECT_EQ(PyTuple_Size(args), 1);
1274 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1275}
1276
1277// METH_KEYWORDS and CALL_FUNCTION_KW
1278
1279TEST_F(TypeExtensionApiTest, MethodsMethKeywordsKwCall) {
1280 ternaryfunc meth = [](PyObject* self, PyObject* args, PyObject* kwargs) {
1281 if (kwargs == nullptr) return PyTuple_Pack(2, self, args);
1282 return PyTuple_Pack(3, self, args, kwargs);
1283 };
1284 static PyMethodDef methods[] = {
1285 {"keywords", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
1286 METH_VARARGS | METH_KEYWORDS},
1287 {nullptr}};
1288 PyType_Slot slots[] = {
1289 {Py_tp_methods, methods},
1290 {0, nullptr},
1291 };
1292
1293 static PyType_Spec spec;
1294 spec = {
1295 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1296 };
1297
1298 PyObjectPtr type(PyType_FromSpec(&spec));
1299 ASSERT_NE(type, nullptr);
1300 testing::moduleSet("__main__", "C", type);
1301 PyRun_SimpleString(R"(
1302self = C()
1303result = self.keywords(1234, kwarg=5678)
1304)");
1305
1306 PyObjectPtr self(testing::mainModuleGet("self"));
1307 PyObjectPtr result(testing::mainModuleGet("result"));
1308 ASSERT_NE(result, nullptr);
1309 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1310 ASSERT_EQ(PyTuple_Size(result), 3);
1311 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1312
1313 PyObject* args = PyTuple_GetItem(result, 1);
1314 EXPECT_TRUE(PyTuple_CheckExact(args));
1315 EXPECT_EQ(PyTuple_Size(args), 1);
1316 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1317
1318 PyObject* kwargs = PyTuple_GetItem(result, 2);
1319 EXPECT_TRUE(PyDict_CheckExact(kwargs));
1320 ASSERT_EQ(PyDict_Size(kwargs), 1);
1321 PyObject* item = PyDict_GetItemString(kwargs, "kwarg");
1322 EXPECT_TRUE(isLongEqualsLong(item, 5678));
1323}
1324
1325// METH_KEYWORDS and CALL_FUNCTION_EX
1326
1327TEST_F(TypeExtensionApiTest, MethodsMethKeywordsExCall) {
1328 ternaryfunc meth = [](PyObject* self, PyObject* args, PyObject* kwargs) {
1329 return PyTuple_Pack(3, self, args, kwargs);
1330 };
1331 static PyMethodDef methods[] = {
1332 {"keywords", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
1333 METH_VARARGS | METH_KEYWORDS},
1334 {nullptr}};
1335 PyType_Slot slots[] = {
1336 {Py_tp_methods, methods},
1337 {0, nullptr},
1338 };
1339
1340 static PyType_Spec spec;
1341 spec = {
1342 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1343 };
1344
1345 PyObjectPtr type(PyType_FromSpec(&spec));
1346 ASSERT_NE(type, nullptr);
1347 testing::moduleSet("__main__", "C", type);
1348 PyRun_SimpleString(R"(
1349self = C()
1350result = self.keywords(*[1234], kwarg=5678)
1351)");
1352
1353 PyObjectPtr self(testing::mainModuleGet("self"));
1354 PyObjectPtr result(testing::mainModuleGet("result"));
1355 ASSERT_NE(result, nullptr);
1356 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1357 ASSERT_EQ(PyTuple_Size(result), 3);
1358 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1359
1360 PyObject* args = PyTuple_GetItem(result, 1);
1361 EXPECT_TRUE(PyTuple_CheckExact(args));
1362 EXPECT_EQ(PyTuple_Size(args), 1);
1363 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1364
1365 PyObject* kwargs = PyTuple_GetItem(result, 2);
1366 EXPECT_TRUE(PyDict_CheckExact(kwargs));
1367 ASSERT_EQ(PyDict_Size(kwargs), 1);
1368 PyObject* item = PyDict_GetItemString(kwargs, "kwarg");
1369 EXPECT_TRUE(isLongEqualsLong(item, 5678));
1370}
1371
1372TEST_F(TypeExtensionApiTest, MethodsMethKeywordsExEmptyKwargsCall) {
1373 ternaryfunc meth = [](PyObject* self, PyObject* args, PyObject* kwargs) {
1374 if (kwargs == nullptr) return PyTuple_Pack(2, self, args);
1375 return PyTuple_Pack(3, self, args, kwargs);
1376 };
1377 static PyMethodDef methods[] = {
1378 {"keywords", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
1379 METH_VARARGS | METH_KEYWORDS},
1380 {nullptr}};
1381 PyType_Slot slots[] = {
1382 {Py_tp_methods, methods},
1383 {0, nullptr},
1384 };
1385
1386 static PyType_Spec spec;
1387 spec = {
1388 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1389 };
1390
1391 PyObjectPtr type(PyType_FromSpec(&spec));
1392 ASSERT_NE(type, nullptr);
1393 testing::moduleSet("__main__", "C", type);
1394 PyRun_SimpleString(R"(
1395self = C()
1396result = self.keywords(*[1234], *{})
1397)");
1398
1399 PyObjectPtr self(testing::mainModuleGet("self"));
1400 PyObjectPtr result(testing::mainModuleGet("result"));
1401 ASSERT_NE(result, nullptr);
1402 ASSERT_EQ(PyTuple_CheckExact(result), 1);
1403 ASSERT_EQ(PyTuple_Size(result), 2);
1404 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
1405
1406 PyObject* args = PyTuple_GetItem(result, 1);
1407 EXPECT_TRUE(PyTuple_CheckExact(args));
1408 EXPECT_EQ(PyTuple_Size(args), 1);
1409 EXPECT_EQ(PyLong_AsLong(PyTuple_GetItem(args, 0)), 1234);
1410}
1411
1412TEST_F(TypeExtensionApiTest, GetObjectCreatedInManagedCode) {
1413 static PyType_Slot slots[1];
1414 slots[0] = {0, nullptr};
1415 static PyType_Spec spec;
1416 spec = {
1417 "__main__.Foo", 0, 0, Py_TPFLAGS_DEFAULT, slots,
1418 };
1419 PyObjectPtr type(PyType_FromSpec(&spec));
1420 ASSERT_NE(type, nullptr);
1421 ASSERT_EQ(PyType_CheckExact(type), 1);
1422 ASSERT_EQ(moduleSet("__main__", "Foo", type), 0);
1423
1424 // This is similar to CallExtensionTypeReturnsExtensionInstancePyro, but it
1425 // tests the RawObject -> PyObject* path for objects that were created on the
1426 // managed heap and had no corresponding PyObject* before the call to
1427 // moduleGet().
1428 ASSERT_EQ(PyRun_SimpleString("f = Foo()"), 0);
1429 PyObjectPtr foo(mainModuleGet("f"));
1430 EXPECT_NE(foo, nullptr);
1431}
1432
1433TEST_F(TypeExtensionApiTest, GenericNewReturnsExtensionInstance) {
1434 struct BarObject {
1435 PyObject_HEAD
1436 };
1437 PyType_Slot slots[] = {
1438 {Py_tp_alloc, reinterpret_cast<void*>(PyType_GenericAlloc)},
1439 {Py_tp_new, reinterpret_cast<void*>(PyType_GenericNew)},
1440 {Py_tp_dealloc, reinterpret_cast<void*>(deallocLeafObject)},
1441 {0, nullptr},
1442 };
1443 static PyType_Spec spec;
1444 spec = {
1445 "foo.Bar", sizeof(BarObject), 0, Py_TPFLAGS_DEFAULT, slots,
1446 };
1447 PyObjectPtr type(PyType_FromSpec(&spec));
1448 ASSERT_NE(type, nullptr);
1449 ASSERT_TRUE(PyType_CheckExact(type));
1450
1451 auto new_func = reinterpret_cast<newfunc>(
1452 PyType_GetSlot(reinterpret_cast<PyTypeObject*>(type.get()), Py_tp_new));
1453 PyObjectPtr bar(
1454 new_func(reinterpret_cast<PyTypeObject*>(type.get()), nullptr, nullptr));
1455 EXPECT_NE(bar, nullptr);
1456}
1457
1458// Given one slot id and a function pointer to go with it, create a Bar type
1459// containing that slot.
1460template <typename T>
1461static void createTypeWithSlotAndBase(const char* type_name, int slot, T pfunc,
1462 PyObject* base) {
1463 static PyType_Slot slots[2];
1464 slots[0] = {slot, reinterpret_cast<void*>(pfunc)};
1465 slots[1] = {0, nullptr};
1466 static PyType_Spec spec;
1467 static char qualname[100];
1468 std::sprintf(qualname, "__main__.%s", type_name);
1469 unsigned int flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
1470 spec = {
1471 qualname, 0, 0, flags, slots,
1472 };
1473 PyObject* tp;
1474 if (base == nullptr) {
1475 tp = PyType_FromSpec(&spec);
1476 } else {
1477 PyObjectPtr bases(PyTuple_Pack(1, base));
1478 tp = PyType_FromSpecWithBases(&spec, bases);
1479 }
1480 PyObjectPtr type(tp);
1481 ASSERT_NE(type, nullptr);
1482 ASSERT_EQ(PyType_CheckExact(type), 1);
1483 ASSERT_EQ(moduleSet("__main__", type_name, type), 0);
1484}
1485
1486template <typename T>
1487static void createTypeWithSlot(const char* type_name, int slot, T pfunc) {
1488 createTypeWithSlotAndBase(type_name, slot, pfunc, nullptr);
1489}
1490
1491TEST_F(TypeExtensionApiTest, CallReverseBinarySlotSwapsArguments) {
1492 binaryfunc add_func = [](PyObject* a, PyObject* b) {
1493 return PyTuple_Pack(2, a, b);
1494 };
1495 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_add, add_func));
1496
1497 ASSERT_EQ(PyRun_SimpleString(R"(
1498instance = Bar()
1499left, right = instance.__radd__(12)
1500)"),
1501 0);
1502
1503 PyObjectPtr instance(mainModuleGet("instance"));
1504 PyObjectPtr left(mainModuleGet("left"));
1505 PyObjectPtr right(mainModuleGet("right"));
1506 EXPECT_TRUE(isLongEqualsLong(left, 12));
1507 EXPECT_EQ(right, instance);
1508}
1509
1510TEST_F(TypeExtensionApiTest, CallBinarySlotFromManagedCode) {
1511 binaryfunc add_func = [](PyObject* a, PyObject* b) {
1512 PyObjectPtr num(PyLong_FromLong(24));
1513 return PyLong_Check(a) ? PyNumber_Add(a, num) : PyNumber_Add(num, b);
1514 };
1515 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_add, add_func));
1516
1517 ASSERT_EQ(PyRun_SimpleString(R"(
1518b = Bar()
1519r1 = b.__add__(12)
1520r2 = Bar.__add__(b, 24)
1521r3 = 1000 + b
1522args = (b, 42)
1523r4 = Bar.__add__(*args)
1524kwargs = {}
1525r5 = b.__add__(100, **kwargs)
1526b += -12
1527)"),
1528 0);
1529
1530 PyObjectPtr r1(mainModuleGet("r1"));
1531 EXPECT_TRUE(isLongEqualsLong(r1, 36));
1532
1533 PyObjectPtr r2(mainModuleGet("r2"));
1534 EXPECT_TRUE(isLongEqualsLong(r2, 48));
1535
1536 PyObjectPtr r3(mainModuleGet("r3"));
1537 EXPECT_TRUE(isLongEqualsLong(r3, 1024));
1538
1539 PyObjectPtr r4(mainModuleGet("r4"));
1540 EXPECT_TRUE(isLongEqualsLong(r4, 66));
1541
1542 PyObjectPtr r5(mainModuleGet("r5"));
1543 EXPECT_TRUE(isLongEqualsLong(r5, 124));
1544
1545 PyObjectPtr b(mainModuleGet("b"));
1546 EXPECT_TRUE(isLongEqualsLong(b, 12));
1547}
1548
1549TEST_F(TypeExtensionApiTest, CallBinarySlotWithKwargsRaisesTypeError) {
1550 binaryfunc dummy_add = [](PyObject*, PyObject*) -> PyObject* {
1551 EXPECT_TRUE(false) << "Shouldn't be called";
1552 Py_RETURN_NONE;
1553 };
1554 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_add, dummy_add));
1555
1556 // TODO(T40700664): Use PyRun_String() so we can directly inspect the thrown
1557 // exception(s).
1558 ASSERT_EQ(PyRun_SimpleString(R"(
1559b = Bar()
1560try:
1561 b.__add__(a=2)
1562 raise RuntimeError("call didn't throw")
1563except TypeError:
1564 pass
1565
1566try:
1567 kwargs = {'a': 2}
1568 b.__add__(**kwargs)
1569 raise RuntimeError("call didn't throw")
1570except TypeError:
1571 pass
1572)"),
1573 0);
1574}
1575
1576TEST_F(TypeExtensionApiTest, CallHashSlotFromManagedCode) {
1577 hashfunc hash_func = [](PyObject*) -> Py_hash_t { return 0xba5eba11; };
1578 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_hash, hash_func));
1579
1580 ASSERT_EQ(PyRun_SimpleString(R"(
1581b = Bar()
1582h1 = b.__hash__()
1583h2 = Bar.__hash__(b)
1584)"),
1585 0);
1586
1587 PyObjectPtr h1(mainModuleGet("h1"));
1588 EXPECT_TRUE(isLongEqualsLong(h1, 0xba5eba11));
1589
1590 PyObjectPtr h2(mainModuleGet("h2"));
1591 EXPECT_TRUE(isLongEqualsLong(h2, 0xba5eba11));
1592}
1593
1594static PyObject* abortingTernaryFunc(PyObject*, PyObject*, PyObject*) {
1595 std::fprintf(stderr, "%s should not have been called!\n", __func__);
1596 std::abort();
1597}
1598
1599TEST_F(TypeExtensionApiTest, CallCallSlotWithMismatchedSelfRaisesTypeError) {
1600 ASSERT_NO_FATAL_FAILURE(
1601 createTypeWithSlot("MyType", Py_tp_call, &abortingTernaryFunc));
1602 PyObjectPtr my_type(mainModuleGet("MyType"));
1603 PyObjectPtr dunder_call(PyObject_GetAttrString(my_type, "__call__"));
1604 PyObjectPtr arg(PyLong_FromLong(5));
1605 PyObjectPtr call_result(
1606 PyObject_CallFunctionObjArgs(dunder_call, arg.get(), nullptr));
1607 EXPECT_EQ(call_result, nullptr);
1608 ASSERT_NE(PyErr_Occurred(), nullptr);
1609 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
1610}
1611
1612TEST_F(TypeExtensionApiTest, CallNewSlotWithNonTypeClsRaisesTypeError) {
1613 ASSERT_NO_FATAL_FAILURE(
1614 createTypeWithSlot("MyType", Py_tp_new, &abortingTernaryFunc));
1615 PyObjectPtr my_type(mainModuleGet("MyType"));
1616 PyObjectPtr dunder_call(PyObject_GetAttrString(my_type, "__new__"));
1617 PyObjectPtr arg(PyLong_FromLong(5));
1618 PyObjectPtr call_result(
1619 PyObject_CallFunctionObjArgs(dunder_call, arg.get(), nullptr));
1620 EXPECT_EQ(call_result, nullptr);
1621 ASSERT_NE(PyErr_Occurred(), nullptr);
1622 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
1623}
1624
1625TEST_F(TypeExtensionApiTest, CallNewSlotWithNonSubclassClsRaisesTypeError) {
1626 ASSERT_NO_FATAL_FAILURE(
1627 createTypeWithSlot("MyType", Py_tp_new, &abortingTernaryFunc));
1628 PyObjectPtr my_type(mainModuleGet("MyType"));
1629 PyObjectPtr dunder_call(PyObject_GetAttrString(my_type, "__new__"));
1630 PyObjectPtr arg(borrow(reinterpret_cast<PyObject*>(&PyType_Type)));
1631 PyObjectPtr call_result(
1632 PyObject_CallFunctionObjArgs(dunder_call, arg.get(), nullptr));
1633 EXPECT_EQ(call_result, nullptr);
1634 ASSERT_NE(PyErr_Occurred(), nullptr);
1635 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
1636}
1637
1638TEST_F(TypeExtensionApiTest, CallCallSlotFromManagedCode) {
1639 ternaryfunc call_func = [](PyObject* self, PyObject* args, PyObject* kwargs) {
1640 return PyTuple_Pack(3, self, args, kwargs ? kwargs : Py_None);
1641 };
1642 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_call, call_func));
1643
1644 ASSERT_EQ(PyRun_SimpleString(R"(
1645b = Bar()
1646r1 = b.__call__()
1647r2 = b.__call__('a', 'b', c='see')
1648r3 = b('hello!')
1649args=(b,"an argument")
1650r4 = Bar.__call__(*args)
1651)"),
1652 0);
1653
1654 PyObjectPtr b(mainModuleGet("b"));
1655 PyObjectPtr r1(mainModuleGet("r1"));
1656 ASSERT_EQ(PyTuple_Check(r1), 1);
1657 ASSERT_EQ(PyTuple_Size(r1), 3);
1658 EXPECT_EQ(PyTuple_GetItem(r1, 0), b);
1659 PyObject* tmp = PyTuple_GetItem(r1, 1);
1660 ASSERT_EQ(PyTuple_Check(tmp), 1);
1661 EXPECT_EQ(PyTuple_Size(tmp), 0);
1662 EXPECT_EQ(PyTuple_GetItem(r1, 2), Py_None);
1663
1664 PyObjectPtr r2(mainModuleGet("r2"));
1665 ASSERT_EQ(PyTuple_Check(r2), 1);
1666 ASSERT_EQ(PyTuple_Size(r2), 3);
1667 EXPECT_EQ(PyTuple_GetItem(r2, 0), b);
1668 tmp = PyTuple_GetItem(r2, 1);
1669 ASSERT_EQ(PyTuple_Check(tmp), 1);
1670 ASSERT_EQ(PyTuple_Size(tmp), 2);
1671 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(tmp, 0), "a"));
1672 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(tmp, 1), "b"));
1673 tmp = PyTuple_GetItem(r2, 2);
1674 ASSERT_EQ(PyDict_Check(tmp), 1);
1675 PyObjectPtr key(PyUnicode_FromString("c"));
1676 EXPECT_TRUE(isUnicodeEqualsCStr(PyDict_GetItem(tmp, key), "see"));
1677
1678 PyObjectPtr r3(mainModuleGet("r3"));
1679 ASSERT_EQ(PyTuple_Check(r3), 1);
1680 ASSERT_EQ(PyTuple_Size(r3), 3);
1681 EXPECT_EQ(PyTuple_GetItem(r3, 0), b);
1682 tmp = PyTuple_GetItem(r3, 1);
1683 ASSERT_EQ(PyTuple_Check(tmp), 1);
1684 ASSERT_EQ(PyTuple_Size(tmp), 1);
1685 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(tmp, 0), "hello!"));
1686 EXPECT_EQ(PyTuple_GetItem(r3, 2), Py_None);
1687
1688 PyObjectPtr r4(mainModuleGet("r4"));
1689 ASSERT_EQ(PyTuple_Check(r4), 1);
1690 ASSERT_EQ(PyTuple_Size(r4), 3);
1691 EXPECT_EQ(PyTuple_GetItem(r4, 0), b);
1692 tmp = PyTuple_GetItem(r4, 1);
1693 ASSERT_EQ(PyTuple_Check(tmp), 1);
1694 ASSERT_EQ(PyTuple_Size(tmp), 1);
1695 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(tmp, 0), "an argument"));
1696 EXPECT_EQ(PyTuple_GetItem(r4, 2), Py_None);
1697}
1698
1699TEST_F(TypeExtensionApiTest, CallGetattroSlotFromManagedCode) {
1700 getattrofunc getattr_func = [](PyObject* self, PyObject* name) {
1701 return PyTuple_Pack(2, name, self);
1702 };
1703 ASSERT_NO_FATAL_FAILURE(
1704 createTypeWithSlot("Bar", Py_tp_getattro, getattr_func));
1705
1706 ASSERT_EQ(PyRun_SimpleString(R"(
1707b = Bar()
1708r0 = b.foo_bar
1709
1710def foo(b):
1711 return b.bar_baz
1712r1 = foo(b)
1713)"),
1714 0);
1715
1716 PyObjectPtr b(mainModuleGet("b"));
1717 ASSERT_NE(b, nullptr);
1718 PyObjectPtr r0(mainModuleGet("r0"));
1719 ASSERT_NE(r0, nullptr);
1720 ASSERT_EQ(PyTuple_Check(r0), 1);
1721 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(r0, 0), "foo_bar"));
1722 EXPECT_EQ(PyTuple_GetItem(r0, 1), b);
1723
1724 PyObjectPtr r1(mainModuleGet("r1"));
1725 ASSERT_NE(r1, nullptr);
1726 ASSERT_EQ(PyTuple_Check(r1), 1);
1727 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(r1, 0), "bar_baz"));
1728 EXPECT_EQ(PyTuple_GetItem(r0, 1), b);
1729}
1730
1731// Pyro-only due to
1732// https://github.com/python/cpython/commit/4dcdb78c6ffd203c9d72ef41638cc4a0e3857adf
1733TEST_F(TypeExtensionApiTest, CallSetattroSlotFromManagedCodePyro) {
1734 setattrofunc setattr_func = [](PyObject* self, PyObject* name,
1735 PyObject* value) {
1736 PyObjectPtr tuple(value ? PyTuple_Pack(3, self, name, value)
1737 : PyTuple_Pack(2, self, name));
1738 const char* var = value ? "set_attr" : "del_attr";
1739 moduleSet("__main__", var, tuple);
1740 return 0;
1741 };
1742 ASSERT_NO_FATAL_FAILURE(
1743 createTypeWithSlot("Bar", Py_tp_setattro, setattr_func));
1744
1745 ASSERT_EQ(PyRun_SimpleString(R"(
1746b = Bar()
1747r1 = b.__setattr__("attr", 1234)
1748)"),
1749 0);
1750
1751 PyObjectPtr b(mainModuleGet("b"));
1752 PyObjectPtr r1(mainModuleGet("r1"));
1753 EXPECT_EQ(r1, Py_None);
1754 PyObjectPtr set_attr(mainModuleGet("set_attr"));
1755 ASSERT_EQ(PyTuple_Check(set_attr), 1);
1756 ASSERT_EQ(PyTuple_Size(set_attr), 3);
1757 EXPECT_EQ(PyTuple_GetItem(set_attr, 0), b);
1758 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(set_attr, 1), "attr"));
1759 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(set_attr, 2), 1234));
1760
1761 ASSERT_EQ(PyRun_SimpleString(R"(r2 = b.__delattr__("other attr"))"), 0);
1762 PyObjectPtr r2(mainModuleGet("r2"));
1763 EXPECT_EQ(r2, Py_None);
1764 PyObjectPtr del_attr(mainModuleGet("del_attr"));
1765 ASSERT_EQ(PyTuple_Check(del_attr), 1);
1766 ASSERT_EQ(PyTuple_Size(del_attr), 2);
1767 EXPECT_EQ(PyTuple_GetItem(del_attr, 0), b);
1768 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(del_attr, 1), "other attr"));
1769}
1770
1771TEST_F(TypeExtensionApiTest, CallRichcompareSlotFromManagedCode) {
1772 richcmpfunc cmp_func = [](PyObject* self, PyObject* other, int op) {
1773 PyObjectPtr op_obj(PyLong_FromLong(op));
1774 return PyTuple_Pack(3, self, other, op_obj.get());
1775 };
1776 ASSERT_NO_FATAL_FAILURE(
1777 createTypeWithSlot("Bar", Py_tp_richcompare, cmp_func));
1778
1779 ASSERT_EQ(PyRun_SimpleString(R"(
1780b = Bar()
1781r1 = b.__eq__("equal")
1782r2 = b.__gt__(0xcafe)
1783)"),
1784 0);
1785
1786 PyObjectPtr b(mainModuleGet("b"));
1787 PyObjectPtr r1(mainModuleGet("r1"));
1788 ASSERT_EQ(PyTuple_Check(r1), 1);
1789 ASSERT_EQ(PyTuple_Size(r1), 3);
1790 EXPECT_EQ(PyTuple_GetItem(r1, 0), b);
1791 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(r1, 1), "equal"));
1792 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 2), Py_EQ));
1793
1794 PyObjectPtr r2(mainModuleGet("r2"));
1795 ASSERT_EQ(PyTuple_Check(r2), 1);
1796 ASSERT_EQ(PyTuple_Size(r2), 3);
1797 EXPECT_EQ(PyTuple_GetItem(r2, 0), b);
1798 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r2, 1), 0xcafe));
1799 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r2, 2), Py_GT));
1800}
1801
1802TEST_F(TypeExtensionApiTest, CallNextSlotFromManagedCode) {
1803 unaryfunc next_func = [](PyObject* self) {
1804 Py_INCREF(self);
1805 return self;
1806 };
1807 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_iternext, next_func));
1808
1809 ASSERT_EQ(PyRun_SimpleString(R"(
1810b = Bar()
1811r = b.__next__()
1812)"),
1813 0);
1814
1815 PyObjectPtr b(mainModuleGet("b"));
1816 PyObjectPtr r(mainModuleGet("r"));
1817 EXPECT_EQ(r, b);
1818}
1819
1820TEST_F(TypeExtensionApiTest, NextSlotReturningNullRaisesStopIteration) {
1821 unaryfunc next_func = [](PyObject*) -> PyObject* { return nullptr; };
1822 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_iternext, next_func));
1823
1824 ASSERT_EQ(PyRun_SimpleString(R"(
1825caught = False
1826try:
1827 Bar().__next__()
1828except StopIteration:
1829 caught = True
1830)"),
1831 0);
1832
1833 PyObjectPtr caught(mainModuleGet("caught"));
1834 EXPECT_EQ(caught, Py_True);
1835}
1836
1837TEST_F(TypeExtensionApiTest, CallDescrGetSlotFromManagedCode) {
1838 descrgetfunc get_func = [](PyObject* self, PyObject* instance,
1839 PyObject* owner) {
1840 return PyTuple_Pack(3, self, instance, owner);
1841 };
1842 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_descr_get, get_func));
1843
1844 ASSERT_EQ(PyRun_SimpleString(R"(
1845b = Bar()
1846b2 = Bar()
1847r = b.__get__(b2, Bar)
1848)"),
1849 0);
1850
1851 PyObjectPtr bar(mainModuleGet("Bar"));
1852 PyObjectPtr b(mainModuleGet("b"));
1853 PyObjectPtr b2(mainModuleGet("b2"));
1854 PyObjectPtr r(mainModuleGet("r"));
1855 ASSERT_EQ(PyTuple_Check(r), 1);
1856 ASSERT_EQ(PyTuple_Size(r), 3);
1857 EXPECT_EQ(PyTuple_GetItem(r, 0), b);
1858 EXPECT_EQ(PyTuple_GetItem(r, 1), b2);
1859 EXPECT_EQ(PyTuple_GetItem(r, 2), bar);
1860}
1861
1862TEST_F(TypeExtensionApiTest, DescrGetSlotWithNonesRaisesTypeError) {
1863 descrgetfunc get_func = [](PyObject*, PyObject*, PyObject*) -> PyObject* {
1864 EXPECT_TRUE(false) << "Shouldn't be called";
1865 return nullptr;
1866 };
1867 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_descr_get, get_func));
1868
1869 // TODO(T40700664): Use PyRun_String() so we can inspect the exception more
1870 // directly.
1871 ASSERT_EQ(PyRun_SimpleString(R"(
1872b = Bar()
1873exc = None
1874try:
1875 b.__get__(None, None)
1876except TypeError as e:
1877 exc = e
1878)"),
1879 0);
1880 PyObjectPtr exc(mainModuleGet("exc"));
1881 EXPECT_EQ(PyErr_GivenExceptionMatches(exc, PyExc_TypeError), 1);
1882}
1883
1884TEST_F(TypeExtensionApiTest, CallDescrSetSlotFromManagedCode) {
1885 descrsetfunc set_func = [](PyObject* /* self */, PyObject* obj,
1886 PyObject* value) -> int {
1887 EXPECT_TRUE(isLongEqualsLong(obj, 123));
1888 EXPECT_TRUE(isLongEqualsLong(value, 456));
1889 return 0;
1890 };
1891 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_descr_set, set_func));
1892
1893 ASSERT_EQ(PyRun_SimpleString(R"(
1894b = Bar()
1895b.__set__(123, 456)
1896)"),
1897 0);
1898}
1899
1900TEST_F(TypeExtensionApiTest, CallDescrDeleteSlotFromManagedCode) {
1901 descrsetfunc set_func = [](PyObject* /* self */, PyObject* obj,
1902 PyObject* value) -> int {
1903 EXPECT_TRUE(isLongEqualsLong(obj, 24));
1904 EXPECT_EQ(value, nullptr);
1905 return 0;
1906 };
1907 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_descr_set, set_func));
1908
1909 ASSERT_EQ(PyRun_SimpleString(R"(
1910b = Bar()
1911b.__delete__(24)
1912)"),
1913 0);
1914}
1915
1916TEST_F(TypeExtensionApiTest, CallInitSlotFromManagedCode) {
1917 initproc init_func = [](PyObject* /* self */, PyObject* args,
1918 PyObject* kwargs) -> int {
1919 moduleSet("__main__", "args", args);
1920 moduleSet("__main__", "kwargs", kwargs);
1921 return 0;
1922 };
1923 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_init, init_func));
1924
1925 ASSERT_EQ(PyRun_SimpleString(R"(
1926b = Bar.__new__(Bar)
1927b.__init__(123, four=4)
1928)"),
1929 0);
1930
1931 PyObjectPtr args(mainModuleGet("args"));
1932 ASSERT_NE(args, nullptr);
1933 ASSERT_EQ(PyTuple_Check(args), 1);
1934 ASSERT_EQ(PyTuple_Size(args), 1);
1935 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(args, 0), 123));
1936
1937 PyObjectPtr kwargs(mainModuleGet("kwargs"));
1938 ASSERT_NE(kwargs, nullptr);
1939 ASSERT_EQ(PyDict_Check(kwargs), 1);
1940 ASSERT_EQ(PyDict_Size(kwargs), 1);
1941 EXPECT_TRUE(isLongEqualsLong(PyDict_GetItemString(kwargs, "four"), 4));
1942}
1943
1944TEST_F(TypeExtensionApiTest, CallDelSlotFromManagedCode) {
1945 destructor del_func = [](PyObject* /* self */) {
1946 moduleSet("__main__", "called", Py_True);
1947 };
1948 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_del, del_func));
1949
1950 ASSERT_EQ(PyRun_SimpleString(R"(
1951bar = Bar()
1952)"),
1953 0);
1954 PyObjectPtr bar_type(mainModuleGet("Bar"));
1955 PyObject* bar = mainModuleGet("bar");
1956 auto func = reinterpret_cast<destructor>(PyType_GetSlot(
1957 reinterpret_cast<PyTypeObject*>(bar_type.get()), Py_tp_dealloc));
1958 (*func)(bar);
1959 PyObjectPtr called(mainModuleGet("called"));
1960 EXPECT_EQ(called, Py_True);
1961}
1962
1963TEST_F(TypeExtensionApiTest, CallTernarySlotFromManagedCode) {
1964 ternaryfunc pow_func = [](PyObject* self, PyObject* value, PyObject* mod) {
1965 return PyTuple_Pack(3, self, value, mod);
1966 };
1967 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_power, pow_func));
1968
1969 ASSERT_EQ(PyRun_SimpleString(R"(
1970b = Bar()
1971r1 = b.__pow__(123, 456)
1972r2 = b.__pow__(789)
1973)"),
1974 0);
1975 PyObjectPtr b(mainModuleGet("b"));
1976 PyObjectPtr r1(mainModuleGet("r1"));
1977 ASSERT_EQ(PyTuple_Check(r1), 1);
1978 ASSERT_EQ(PyTuple_Size(r1), 3);
1979 EXPECT_EQ(PyTuple_GetItem(r1, 0), b);
1980 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 1), 123));
1981 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 2), 456));
1982
1983 PyObjectPtr r2(mainModuleGet("r2"));
1984 ASSERT_EQ(PyTuple_Check(r2), 1);
1985 ASSERT_EQ(PyTuple_Size(r1), 3);
1986 EXPECT_EQ(PyTuple_GetItem(r2, 0), b);
1987 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r2, 1), 789));
1988 EXPECT_EQ(PyTuple_GetItem(r2, 2), Py_None);
1989}
1990
1991TEST_F(TypeExtensionApiTest, CallInquirySlotFromManagedCode) {
1992 inquiry bool_func = [](PyObject* self) {
1993 PyObjectPtr b(mainModuleGet("b"));
1994 EXPECT_EQ(self, b);
1995 return 1;
1996 };
1997 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_bool, bool_func));
1998
1999 ASSERT_EQ(PyRun_SimpleString(R"(
2000b = Bar()
2001r = b.__bool__()
2002 )"),
2003 0);
2004 PyObjectPtr r(mainModuleGet("r"));
2005 EXPECT_EQ(r, Py_True);
2006}
2007
2008TEST_F(TypeExtensionApiTest, CallObjobjargSlotFromManagedCode) {
2009 objobjargproc set_func = [](PyObject* self, PyObject* key, PyObject* value) {
2010 PyObjectPtr b(mainModuleGet("b"));
2011 EXPECT_EQ(self, b);
2012 moduleSet("__main__", "key", key);
2013 moduleSet("__main__", "value", value);
2014 return 0;
2015 };
2016 ASSERT_NO_FATAL_FAILURE(
2017 createTypeWithSlot("Bar", Py_mp_ass_subscript, set_func));
2018
2019 ASSERT_EQ(PyRun_SimpleString(R"(
2020b = Bar()
2021r = b.__setitem__("some key", "a value")
2022)"),
2023 0);
2024 PyObjectPtr r(mainModuleGet("r"));
2025 EXPECT_EQ(r, Py_None);
2026
2027 PyObjectPtr key(mainModuleGet("key"));
2028 EXPECT_TRUE(isUnicodeEqualsCStr(key, "some key"));
2029
2030 PyObjectPtr value(mainModuleGet("value"));
2031 EXPECT_TRUE(isUnicodeEqualsCStr(value, "a value"));
2032}
2033
2034TEST_F(TypeExtensionApiTest, CallObjobjSlotFromManagedCode) {
2035 objobjproc contains_func = [](PyObject* self, PyObject* value) {
2036 PyObjectPtr b(mainModuleGet("b"));
2037 EXPECT_EQ(self, b);
2038 moduleSet("__main__", "value", value);
2039 return 123456;
2040 };
2041 ASSERT_NO_FATAL_FAILURE(
2042 createTypeWithSlot("Bar", Py_sq_contains, contains_func));
2043
2044 ASSERT_EQ(PyRun_SimpleString(R"(
2045b = Bar()
2046r = b.__contains__("a key")
2047)"),
2048 0);
2049 PyObjectPtr r(mainModuleGet("r"));
2050 EXPECT_EQ(r, Py_True);
2051
2052 PyObjectPtr value(mainModuleGet("value"));
2053 EXPECT_TRUE(isUnicodeEqualsCStr(value, "a key"));
2054}
2055
2056TEST_F(TypeExtensionApiTest, CallDelitemSlotFromManagedCode) {
2057 objobjargproc del_func = [](PyObject* self, PyObject* key, PyObject* value) {
2058 PyObjectPtr b(mainModuleGet("b"));
2059 EXPECT_EQ(self, b);
2060 EXPECT_EQ(value, nullptr);
2061 moduleSet("__main__", "key", key);
2062 return 0;
2063 };
2064 ASSERT_NO_FATAL_FAILURE(
2065 createTypeWithSlot("Bar", Py_mp_ass_subscript, del_func));
2066
2067 ASSERT_EQ(PyRun_SimpleString(R"(
2068b = Bar()
2069r = b.__delitem__("another key")
2070)"),
2071 0);
2072 PyObjectPtr r(mainModuleGet("r"));
2073 EXPECT_EQ(r, Py_None);
2074
2075 PyObjectPtr key(mainModuleGet("key"));
2076 EXPECT_TRUE(isUnicodeEqualsCStr(key, "another key"));
2077}
2078
2079TEST_F(TypeExtensionApiTest, CallLenSlotFromManagedCode) {
2080 lenfunc len_func = [](PyObject* self) -> Py_ssize_t {
2081 PyObjectPtr b(mainModuleGet("b"));
2082 EXPECT_EQ(self, b);
2083 return 0xdeadbeef;
2084 };
2085 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_sq_length, len_func));
2086
2087 ASSERT_EQ(PyRun_SimpleString(R"(
2088b = Bar()
2089r = b.__len__()
2090)"),
2091 0);
2092 PyObjectPtr r(mainModuleGet("r"));
2093 EXPECT_TRUE(isLongEqualsLong(r, 0xdeadbeef));
2094}
2095
2096TEST_F(TypeExtensionApiTest, CallIndexargSlotFromManagedCode) {
2097 ssizeargfunc mul_func = [](PyObject* self, Py_ssize_t i) {
2098 PyObjectPtr b(mainModuleGet("b"));
2099 EXPECT_EQ(self, b);
2100 return PyLong_FromLong(i * 456);
2101 };
2102 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_sq_repeat, mul_func));
2103
2104 ASSERT_EQ(PyRun_SimpleString(R"(
2105b = Bar()
2106r = b.__mul__(123)
2107)"),
2108 0);
2109 PyObjectPtr r(mainModuleGet("r"));
2110 EXPECT_TRUE(isLongEqualsLong(r, 123 * 456));
2111}
2112
2113TEST_F(TypeExtensionApiTest, CallSqItemSlotFromManagedCode) {
2114 ssizeargfunc item_func = [](PyObject* self, Py_ssize_t i) {
2115 PyObjectPtr b(mainModuleGet("b"));
2116 EXPECT_EQ(self, b);
2117 return PyLong_FromLong(i + 100);
2118 };
2119 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_sq_item, item_func));
2120
2121 ASSERT_EQ(PyRun_SimpleString(R"(
2122b = Bar()
2123r = b.__getitem__(1337)
2124)"),
2125 0);
2126 PyObjectPtr r(mainModuleGet("r"));
2127 EXPECT_TRUE(isLongEqualsLong(r, 1337 + 100));
2128}
2129
2130TEST_F(TypeExtensionApiTest, CallSqSetitemSlotFromManagedCode) {
2131 ssizeobjargproc set_func = [](PyObject* self, Py_ssize_t i, PyObject* value) {
2132 PyObjectPtr b(mainModuleGet("b"));
2133 EXPECT_EQ(self, b);
2134 PyObjectPtr key(PyLong_FromLong(i));
2135 moduleSet("__main__", "key", key);
2136 moduleSet("__main__", "value", value);
2137 return 0;
2138 };
2139 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_sq_ass_item, set_func));
2140
2141 ASSERT_EQ(PyRun_SimpleString(R"(
2142b = Bar()
2143r = b.__setitem__(123, 456)
2144)"),
2145 0);
2146 PyObjectPtr r(mainModuleGet("r"));
2147 EXPECT_EQ(r, Py_None);
2148
2149 PyObjectPtr key(mainModuleGet("key"));
2150 EXPECT_TRUE(isLongEqualsLong(key, 123));
2151
2152 PyObjectPtr value(mainModuleGet("value"));
2153 EXPECT_TRUE(isLongEqualsLong(value, 456));
2154}
2155
2156TEST_F(TypeExtensionApiTest, CallSqDelitemSlotFromManagedCode) {
2157 ssizeobjargproc del_func = [](PyObject* self, Py_ssize_t i, PyObject* value) {
2158 PyObjectPtr b(mainModuleGet("b"));
2159 EXPECT_EQ(self, b);
2160 PyObjectPtr key(PyLong_FromLong(i));
2161 moduleSet("__main__", "key", key);
2162 EXPECT_EQ(value, nullptr);
2163 return 0;
2164 };
2165 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_sq_ass_item, del_func));
2166
2167 ASSERT_EQ(PyRun_SimpleString(R"(
2168b = Bar()
2169r = b.__delitem__(7890)
2170)"),
2171 0);
2172 PyObjectPtr r(mainModuleGet("r"));
2173 EXPECT_EQ(r, Py_None);
2174
2175 PyObjectPtr key(mainModuleGet("key"));
2176 EXPECT_TRUE(isLongEqualsLong(key, 7890));
2177}
2178
2179TEST_F(TypeExtensionApiTest, HashNotImplementedSlotSetsNoneDunderHash) {
2180 ASSERT_NO_FATAL_FAILURE(
2181 createTypeWithSlot("Bar", Py_tp_hash, PyObject_HashNotImplemented));
2182 PyObjectPtr bar(mainModuleGet("Bar"));
2183 PyObjectPtr hash(PyObject_GetAttrString(bar, "__hash__"));
2184 EXPECT_EQ(hash, Py_None);
2185}
2186
2187TEST_F(TypeExtensionApiTest, CallNewSlotFromManagedCode) {
2188 ternaryfunc new_func = [](PyObject* type, PyObject* args, PyObject* kwargs) {
2189 PyObjectPtr name(PyObject_GetAttrString(type, "__name__"));
2190 EXPECT_TRUE(isUnicodeEqualsCStr(name, "Bar"));
2191 EXPECT_EQ(PyTuple_Check(args), 1);
2192 EXPECT_EQ(kwargs, nullptr);
2193 Py_INCREF(args);
2194 return args;
2195 };
2196 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_new, new_func));
2197
2198 ASSERT_EQ(PyRun_SimpleString(R"(
2199r0 = Bar.__new__(Bar, 1, 2, 3)
2200r1 = Bar(1, 2, 3)
2201)"),
2202 0);
2203 PyObjectPtr r0(mainModuleGet("r0"));
2204 ASSERT_EQ(PyTuple_Check(r0), 1);
2205 ASSERT_EQ(PyTuple_Size(r0), 3);
2206 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r0, 0), 1));
2207 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r0, 1), 2));
2208 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r0, 2), 3));
2209
2210 PyObjectPtr r1(mainModuleGet("r1"));
2211 ASSERT_EQ(PyTuple_Check(r1), 1);
2212 ASSERT_EQ(PyTuple_Size(r1), 3);
2213 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 0), 1));
2214 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 1), 2));
2215 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(r1, 2), 3));
2216}
2217
2218TEST_F(TypeExtensionApiTest, NbAddSlotTakesPrecedenceOverSqConcatSlot) {
2219 binaryfunc add_func = [](PyObject* /* self */, PyObject* obj) {
2220 EXPECT_TRUE(isUnicodeEqualsCStr(obj, "foo"));
2221 return PyLong_FromLong(0xf00);
2222 };
2223 binaryfunc concat_func = [](PyObject*, PyObject*) -> PyObject* {
2224 std::abort();
2225 };
2226 static PyType_Slot slots[3];
2227 // Both of these slots map to __add__. nb_add appears in slotdefs first, so it
2228 // wins.
2229 slots[0] = {Py_nb_add, reinterpret_cast<void*>(add_func)};
2230 slots[1] = {Py_sq_concat, reinterpret_cast<void*>(concat_func)};
2231 slots[2] = {0, nullptr};
2232 static PyType_Spec spec;
2233 spec = {
2234 "__main__.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
2235 };
2236 PyObjectPtr type(PyType_FromSpec(&spec));
2237 ASSERT_NE(type, nullptr);
2238 ASSERT_EQ(PyType_CheckExact(type), 1);
2239 ASSERT_EQ(moduleSet("__main__", "Bar", type), 0);
2240
2241 ASSERT_EQ(PyRun_SimpleString(R"(
2242b = Bar()
2243r = b.__add__("foo")
2244)"),
2245 0);
2246 PyObjectPtr r(mainModuleGet("r"));
2247 EXPECT_TRUE(isLongEqualsLong(r, 0xf00));
2248}
2249
2250TEST_F(TypeExtensionApiTest, TypeSlotPropagatesException) {
2251 binaryfunc add_func = [](PyObject*, PyObject*) -> PyObject* {
2252 PyErr_SetString(PyExc_RuntimeError, "hello, there!");
2253 return nullptr;
2254 };
2255 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_nb_add, add_func));
2256
2257 // TODO(T40700664): Use PyRun_String() so we can inspect the exception more
2258 // directly.
2259 EXPECT_EQ(PyRun_SimpleString(R"(
2260exc = None
2261try:
2262 Bar().__add__(1)
2263except RuntimeError as e:
2264 exc = e
2265)"),
2266 0);
2267 PyObjectPtr exc(mainModuleGet("exc"));
2268 EXPECT_EQ(PyErr_GivenExceptionMatches(exc, PyExc_RuntimeError), 1);
2269}
2270
2271typedef void (*verifyfunc)(PyObject*);
2272static verifyfunc createBarTypeWithMembers() {
2273 struct BarObject {
2274 PyObject_HEAD
2275 char t_bool;
2276 char t_byte;
2277 unsigned char t_ubyte;
2278 short t_short;
2279 unsigned short t_ushort;
2280 int t_int;
2281 unsigned int t_uint;
2282 long t_long;
2283 unsigned long t_ulong;
2284 Py_ssize_t t_pyssizet;
2285 float t_float;
2286 double t_double;
2287 const char* t_string;
2288 char t_char;
2289 PyObject* t_object;
2290 PyObject* t_object_null;
2291 long long t_longlong;
2292 unsigned long long t_ulonglong;
2293 };
2294
2295 static const PyMemberDef members[] = {
2296 {"t_bool", T_BOOL, offsetof(BarObject, t_bool)},
2297 {"t_byte", T_BYTE, offsetof(BarObject, t_byte)},
2298 {"t_ubyte", T_UBYTE, offsetof(BarObject, t_ubyte)},
2299 {"t_short", T_SHORT, offsetof(BarObject, t_short)},
2300 {"t_ushort", T_USHORT, offsetof(BarObject, t_ushort)},
2301 {"t_int", T_INT, offsetof(BarObject, t_int)},
2302 {"t_uint", T_UINT, offsetof(BarObject, t_uint)},
2303 {"t_long", T_LONG, offsetof(BarObject, t_long)},
2304 {"t_ulong", T_ULONG, offsetof(BarObject, t_ulong)},
2305 {"t_pyssize", T_PYSSIZET, offsetof(BarObject, t_pyssizet)},
2306 {"t_float", T_FLOAT, offsetof(BarObject, t_float)},
2307 {"t_double", T_DOUBLE, offsetof(BarObject, t_double)},
2308 {"t_string", T_STRING, offsetof(BarObject, t_string)},
2309 {"t_char", T_CHAR, offsetof(BarObject, t_char)},
2310 {"t_object", T_OBJECT, offsetof(BarObject, t_object)},
2311 {"t_object_null", T_OBJECT, offsetof(BarObject, t_object_null)},
2312 {"t_objectex", T_OBJECT_EX, offsetof(BarObject, t_object)},
2313 {"t_objectex_null", T_OBJECT_EX, offsetof(BarObject, t_object_null)},
2314 {"t_longlong", T_LONGLONG, offsetof(BarObject, t_longlong)},
2315 {"t_ulonglong", T_ULONGLONG, offsetof(BarObject, t_ulonglong)},
2316 {"t_int_readonly", T_INT, offsetof(BarObject, t_int), READONLY},
2317 {nullptr},
2318 };
2319 newfunc new_func = [](PyTypeObject* type, PyObject*, PyObject*) {
2320 void* slot = PyType_GetSlot(type, Py_tp_alloc);
2321 return reinterpret_cast<allocfunc>(slot)(type, 0);
2322 };
2323 freefunc dealloc_func = [](void* self_ptr) {
2324 PyObject* self = static_cast<PyObject*>(self_ptr);
2325 BarObject* self_bar = static_cast<BarObject*>(self_ptr);
2326 // Guaranteed to be null or initialized by something
2327 Py_XDECREF(self_bar->t_object);
2328 PyTypeObject* type = Py_TYPE(self);
2329 // Since this object is subtypable (has Py_TPFLAGS_BASETYPE), we must pull
2330 // out tp_free slot instead of calling PyObject_Del.
2331 void* slot = PyType_GetSlot(type, Py_tp_free);
2332 assert(slot != nullptr);
2333 reinterpret_cast<freefunc>(slot)(self);
2334 Py_DECREF(type);
2335 };
2336 initproc init_func = [](PyObject* self, PyObject*, PyObject*) {
2337 BarObject* bar_obj = reinterpret_cast<BarObject*>(self);
2338 bar_obj->t_bool = 1;
2339 bar_obj->t_byte = -12;
2340 bar_obj->t_ubyte = std::numeric_limits<unsigned char>::max();
2341 bar_obj->t_short = -12;
2342 bar_obj->t_ushort = std::numeric_limits<unsigned short>::max();
2343 bar_obj->t_int = -1234;
2344 bar_obj->t_uint = std::numeric_limits<unsigned int>::max();
2345 bar_obj->t_long = -1234;
2346 bar_obj->t_ulong = std::numeric_limits<unsigned long>::max();
2347 bar_obj->t_pyssizet = 1234;
2348 bar_obj->t_float = 1.0;
2349 bar_obj->t_double = 1.0;
2350 bar_obj->t_string = "foo";
2351 bar_obj->t_char = 'a';
2352 bar_obj->t_object = PyList_New(0);
2353 bar_obj->t_object_null = nullptr;
2354 bar_obj->t_longlong = std::numeric_limits<long long>::max();
2355 bar_obj->t_ulonglong = std::numeric_limits<unsigned long long>::max();
2356 return 0;
2357 };
2358 static const PyType_Slot slots[] = {
2359 {Py_tp_new, reinterpret_cast<void*>(new_func)},
2360 {Py_tp_init, reinterpret_cast<void*>(init_func)},
2361 {Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
2362 {Py_tp_members, const_cast<PyMemberDef*>(members)},
2363 {0, nullptr},
2364 };
2365 static PyType_Spec spec = {
2366 "__main__.Bar",
2367 sizeof(BarObject),
2368 0,
2369 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2370 const_cast<PyType_Slot*>(slots),
2371 };
2372 PyObjectPtr type(PyType_FromSpec(&spec));
2373 moduleSet("__main__", "Bar", type);
2374
2375 // Verifies the original values
2376 return [](PyObject* self) {
2377 BarObject* bar_obj = reinterpret_cast<BarObject*>(self);
2378 ASSERT_EQ(bar_obj->t_bool, 1);
2379 ASSERT_EQ(bar_obj->t_byte, -12);
2380 ASSERT_EQ(bar_obj->t_ubyte, std::numeric_limits<unsigned char>::max());
2381 ASSERT_EQ(bar_obj->t_short, -12);
2382 ASSERT_EQ(bar_obj->t_ushort, std::numeric_limits<unsigned short>::max());
2383 ASSERT_EQ(bar_obj->t_int, -1234);
2384 ASSERT_EQ(bar_obj->t_uint, std::numeric_limits<unsigned int>::max());
2385 ASSERT_EQ(bar_obj->t_long, -1234);
2386 ASSERT_EQ(bar_obj->t_ulong, std::numeric_limits<unsigned long>::max());
2387 ASSERT_EQ(bar_obj->t_pyssizet, 1234);
2388 ASSERT_EQ(bar_obj->t_float, 1.0);
2389 ASSERT_EQ(bar_obj->t_double, 1.0);
2390 ASSERT_EQ(bar_obj->t_string, "foo");
2391 ASSERT_EQ(bar_obj->t_char, 'a');
2392 ASSERT_TRUE(PyList_CheckExact(bar_obj->t_object));
2393 ASSERT_EQ(PyList_Size(bar_obj->t_object), 0);
2394 ASSERT_EQ(bar_obj->t_object_null, nullptr);
2395 ASSERT_EQ(bar_obj->t_longlong, std::numeric_limits<long long>::max());
2396 ASSERT_EQ(bar_obj->t_ulonglong,
2397 std::numeric_limits<unsigned long long>::max());
2398 };
2399}
2400
2401TEST_F(TypeExtensionApiTest, MemberBool) {
2402 auto verify_func = createBarTypeWithMembers();
2403 ASSERT_EQ(PyRun_SimpleString(R"(
2404b = Bar()
2405r1 = b.t_bool
2406b.t_bool = False
2407r2 = b.t_bool
2408b.t_bool = r1
2409)"),
2410 0);
2411 PyObjectPtr r1(mainModuleGet("r1"));
2412 ASSERT_EQ(PyBool_Check(r1), 1);
2413 EXPECT_EQ(r1, Py_True);
2414 PyObjectPtr r2(mainModuleGet("r2"));
2415 ASSERT_EQ(PyBool_Check(r2), 1);
2416 EXPECT_EQ(r2, Py_False);
2417 PyObjectPtr b(mainModuleGet("b"));
2418 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2419}
2420
2421TEST_F(TypeExtensionApiTest, MemberByte) {
2422 auto verify_func = createBarTypeWithMembers();
2423 ASSERT_EQ(PyRun_SimpleString(R"(
2424b = Bar()
2425r1 = b.t_byte
2426b.t_byte = 21
2427r2 = b.t_byte
2428b.t_byte = r1
2429)"),
2430 0);
2431 PyObjectPtr r1(mainModuleGet("r1"));
2432 ASSERT_EQ(PyLong_Check(r1), 1);
2433 EXPECT_TRUE(isLongEqualsLong(r1, -12));
2434 PyObjectPtr r2(mainModuleGet("r2"));
2435 ASSERT_EQ(PyLong_Check(r2), 1);
2436 EXPECT_TRUE(isLongEqualsLong(r2, 21));
2437 PyObjectPtr b(mainModuleGet("b"));
2438 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2439}
2440
2441TEST_F(TypeExtensionApiTest, MemberUByte) {
2442 auto verify_func = createBarTypeWithMembers();
2443 ASSERT_EQ(PyRun_SimpleString(R"(
2444b = Bar()
2445r1 = b.t_ubyte
2446b.t_ubyte = 21
2447r2 = b.t_ubyte
2448b.t_ubyte = r1
2449)"),
2450 0);
2451 PyObjectPtr r1(mainModuleGet("r1"));
2452 ASSERT_EQ(PyLong_Check(r1), 1);
2453 EXPECT_TRUE(isLongEqualsLong(r1, std::numeric_limits<unsigned char>::max()));
2454 PyObjectPtr r2(mainModuleGet("r2"));
2455 ASSERT_EQ(PyLong_Check(r2), 1);
2456 EXPECT_TRUE(isLongEqualsLong(r2, 21));
2457 PyObjectPtr b(mainModuleGet("b"));
2458 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2459}
2460
2461TEST_F(TypeExtensionApiTest, MemberShort) {
2462 auto verify_func = createBarTypeWithMembers();
2463 ASSERT_EQ(PyRun_SimpleString(R"(
2464b = Bar()
2465r1 = b.t_short
2466b.t_short = 21
2467r2 = b.t_short
2468b.t_short = r1
2469)"),
2470 0);
2471 PyObjectPtr r1(mainModuleGet("r1"));
2472 ASSERT_EQ(PyLong_Check(r1), 1);
2473 EXPECT_TRUE(isLongEqualsLong(r1, -12));
2474 PyObjectPtr r2(mainModuleGet("r2"));
2475 ASSERT_EQ(PyLong_Check(r2), 1);
2476 EXPECT_TRUE(isLongEqualsLong(r2, 21));
2477 PyObjectPtr b(mainModuleGet("b"));
2478 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2479}
2480
2481TEST_F(TypeExtensionApiTest, MemberUShort) {
2482 auto verify_func = createBarTypeWithMembers();
2483 ASSERT_EQ(PyRun_SimpleString(R"(
2484b = Bar()
2485r1 = b.t_ushort
2486b.t_ushort = 21
2487r2 = b.t_ushort
2488b.t_ushort = r1
2489)"),
2490 0);
2491 PyObjectPtr r1(mainModuleGet("r1"));
2492 ASSERT_EQ(PyLong_Check(r1), 1);
2493 EXPECT_TRUE(isLongEqualsLong(r1, std::numeric_limits<unsigned short>::max()));
2494 PyObjectPtr r2(mainModuleGet("r2"));
2495 ASSERT_EQ(PyLong_Check(r2), 1);
2496 EXPECT_TRUE(isLongEqualsLong(r2, 21));
2497 PyObjectPtr b(mainModuleGet("b"));
2498 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2499}
2500
2501TEST_F(TypeExtensionApiTest, MemberInt) {
2502 auto verify_func = createBarTypeWithMembers();
2503 ASSERT_EQ(PyRun_SimpleString(R"(
2504b = Bar()
2505r1 = b.t_int
2506b.t_int = 4321
2507r2 = b.t_int
2508b.t_int = r1
2509)"),
2510 0);
2511 PyObjectPtr r1(mainModuleGet("r1"));
2512 ASSERT_EQ(PyLong_Check(r1), 1);
2513 EXPECT_TRUE(isLongEqualsLong(r1, -1234));
2514 PyObjectPtr r2(mainModuleGet("r2"));
2515 ASSERT_EQ(PyLong_Check(r2), 1);
2516 EXPECT_TRUE(isLongEqualsLong(r2, 4321));
2517 PyObjectPtr b(mainModuleGet("b"));
2518 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2519}
2520
2521TEST_F(TypeExtensionApiTest, MemberUInt) {
2522 auto verify_func = createBarTypeWithMembers();
2523 ASSERT_EQ(PyRun_SimpleString(R"(
2524b = Bar()
2525r1 = b.t_uint
2526b.t_uint = 4321
2527r2 = b.t_uint
2528b.t_uint = r1
2529)"),
2530 0);
2531 PyObjectPtr r1(mainModuleGet("r1"));
2532 ASSERT_EQ(PyLong_Check(r1), 1);
2533 EXPECT_EQ(PyLong_AsUnsignedLong(r1),
2534 std::numeric_limits<unsigned int>::max());
2535 PyObjectPtr r2(mainModuleGet("r2"));
2536 ASSERT_EQ(PyLong_Check(r2), 1);
2537 EXPECT_EQ(PyLong_AsUnsignedLong(r2), 4321UL);
2538 PyObjectPtr b(mainModuleGet("b"));
2539 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2540}
2541
2542TEST_F(TypeExtensionApiTest, MemberLong) {
2543 auto verify_func = createBarTypeWithMembers();
2544 ASSERT_EQ(PyRun_SimpleString(R"(
2545b = Bar()
2546r1 = b.t_long
2547b.t_long = 4321
2548r2 = b.t_long
2549b.t_long = r1
2550)"),
2551 0);
2552 PyObjectPtr r1(mainModuleGet("r1"));
2553 ASSERT_EQ(PyLong_Check(r1), 1);
2554 EXPECT_TRUE(isLongEqualsLong(r1, -1234));
2555 PyObjectPtr r2(mainModuleGet("r2"));
2556 ASSERT_EQ(PyLong_Check(r2), 1);
2557 EXPECT_TRUE(isLongEqualsLong(r2, 4321));
2558 PyObjectPtr b(mainModuleGet("b"));
2559 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2560}
2561
2562TEST_F(TypeExtensionApiTest, MemberULong) {
2563 auto verify_func = createBarTypeWithMembers();
2564 ASSERT_EQ(PyRun_SimpleString(R"(
2565b = Bar()
2566r1 = b.t_ulong
2567b.t_ulong = 4321
2568r2 = b.t_ulong
2569b.t_ulong = r1
2570)"),
2571 0);
2572 PyObjectPtr r1(mainModuleGet("r1"));
2573 ASSERT_EQ(PyLong_Check(r1), 1);
2574 EXPECT_EQ(PyLong_AsUnsignedLong(r1),
2575 std::numeric_limits<unsigned long>::max());
2576 PyObjectPtr r2(mainModuleGet("r2"));
2577 ASSERT_EQ(PyLong_Check(r2), 1);
2578 EXPECT_EQ(PyLong_AsUnsignedLong(r2), 4321UL);
2579 PyObjectPtr b(mainModuleGet("b"));
2580 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2581}
2582
2583TEST_F(TypeExtensionApiTest, MemberLongLong) {
2584 auto verify_func = createBarTypeWithMembers();
2585 ASSERT_EQ(PyRun_SimpleString(R"(
2586b = Bar()
2587r1 = b.t_longlong
2588b.t_longlong = -4321
2589r2 = b.t_longlong
2590b.t_longlong = r1
2591)"),
2592 0);
2593 PyObjectPtr r1(mainModuleGet("r1"));
2594 ASSERT_EQ(PyLong_Check(r1), 1);
2595 EXPECT_EQ(PyLong_AsLongLong(r1), std::numeric_limits<long long>::max());
2596 PyObjectPtr r2(mainModuleGet("r2"));
2597 ASSERT_EQ(PyLong_Check(r2), 1);
2598 EXPECT_TRUE(isLongEqualsLong(r2, -4321));
2599 PyObjectPtr b(mainModuleGet("b"));
2600 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2601}
2602
2603TEST_F(TypeExtensionApiTest, MemberULongLong) {
2604 auto verify_func = createBarTypeWithMembers();
2605 ASSERT_EQ(PyRun_SimpleString(R"(
2606b = Bar()
2607r1 = b.t_ulonglong
2608b.t_ulonglong = 4321
2609r2 = b.t_ulonglong
2610b.t_ulonglong = r1
2611)"),
2612 0);
2613 PyObjectPtr r1(mainModuleGet("r1"));
2614 ASSERT_EQ(PyLong_Check(r1), 1);
2615 EXPECT_EQ(PyLong_AsUnsignedLongLong(r1),
2616 std::numeric_limits<unsigned long long>::max());
2617 PyObjectPtr r2(mainModuleGet("r2"));
2618 ASSERT_EQ(PyLong_Check(r2), 1);
2619 EXPECT_EQ(PyLong_AsUnsignedLongLong(r2), 4321UL);
2620 PyObjectPtr b(mainModuleGet("b"));
2621 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2622}
2623
2624TEST_F(TypeExtensionApiTest, MemberFloat) {
2625 auto verify_func = createBarTypeWithMembers();
2626 ASSERT_EQ(PyRun_SimpleString(R"(
2627b = Bar()
2628r1 = b.t_float
2629b.t_float = 1.5
2630r2 = b.t_float
2631b.t_float = r1
2632)"),
2633 0);
2634 PyObjectPtr r1(mainModuleGet("r1"));
2635 ASSERT_EQ(PyFloat_Check(r1), 1);
2636 EXPECT_EQ(PyFloat_AsDouble(r1), 1.0);
2637 PyObjectPtr r2(mainModuleGet("r2"));
2638 ASSERT_EQ(PyFloat_Check(r2), 1);
2639 EXPECT_EQ(PyFloat_AsDouble(r2), 1.5);
2640 PyObjectPtr b(mainModuleGet("b"));
2641 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2642}
2643
2644TEST_F(TypeExtensionApiTest, MemberDouble) {
2645 auto verify_func = createBarTypeWithMembers();
2646 ASSERT_EQ(PyRun_SimpleString(R"(
2647b = Bar()
2648r1 = b.t_double
2649b.t_double = 1.5
2650r2 = b.t_double
2651b.t_double = r1
2652)"),
2653 0);
2654 PyObjectPtr r1(mainModuleGet("r1"));
2655 ASSERT_EQ(PyFloat_Check(r1), 1);
2656 EXPECT_EQ(PyFloat_AsDouble(r1), 1.0);
2657 PyObjectPtr r2(mainModuleGet("r2"));
2658 ASSERT_EQ(PyFloat_Check(r2), 1);
2659 EXPECT_EQ(PyFloat_AsDouble(r2), 1.5);
2660 PyObjectPtr b(mainModuleGet("b"));
2661 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2662}
2663
2664TEST_F(TypeExtensionApiTest, MemberChar) {
2665 auto verify_func = createBarTypeWithMembers();
2666 ASSERT_EQ(PyRun_SimpleString(R"(
2667b = Bar()
2668r1 = b.t_char
2669b.t_char = 'b'
2670r2 = b.t_char
2671b.t_char = r1
2672)"),
2673 0);
2674 PyObjectPtr r1(mainModuleGet("r1"));
2675 ASSERT_EQ(PyUnicode_Check(r1), 1);
2676 EXPECT_TRUE(isUnicodeEqualsCStr(r1, "a"));
2677 PyObjectPtr r2(mainModuleGet("r2"));
2678 ASSERT_EQ(PyUnicode_Check(r2), 1);
2679 EXPECT_TRUE(isUnicodeEqualsCStr(r2, "b"));
2680 PyObjectPtr b(mainModuleGet("b"));
2681 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2682}
2683
2684TEST_F(TypeExtensionApiTest, MemberString) {
2685 // T_STRING is read-only
2686 ASSERT_NO_FATAL_FAILURE(createBarTypeWithMembers());
2687 ASSERT_EQ(PyRun_SimpleString(R"(
2688b = Bar()
2689r1 = b.t_string
2690)"),
2691 0);
2692 PyObjectPtr r1(mainModuleGet("r1"));
2693 ASSERT_EQ(PyUnicode_Check(r1), 1);
2694 EXPECT_TRUE(isUnicodeEqualsCStr(r1, "foo"));
2695}
2696
2697TEST_F(TypeExtensionApiTest, MemberStringWithNullReturnsNone) {
2698 struct BarObject {
2699 PyObject_HEAD
2700 char* name;
2701 };
2702 static const PyMemberDef members[] = {
2703 {"name", T_STRING, offsetof(BarObject, name), 0, nullptr},
2704 {nullptr},
2705 };
2706 static const PyType_Slot slots[] = {
2707 {Py_tp_members, const_cast<PyMemberDef*>(members)},
2708 {0, nullptr},
2709 };
2710 static PyType_Spec spec = {
2711 "__main__.Bar",
2712 sizeof(BarObject),
2713 0,
2714 Py_TPFLAGS_DEFAULT,
2715 const_cast<PyType_Slot*>(slots),
2716 };
2717 PyObjectPtr type(PyType_FromSpec(&spec));
2718 ASSERT_TRUE(PyType_Check(type));
2719 ASSERT_EQ(PyErr_Occurred(), nullptr);
2720 ASSERT_EQ(moduleSet("__main__", "Bar", type), 0);
2721
2722 ASSERT_EQ(PyRun_SimpleString(R"(
2723b = Bar()
2724none = b.name
2725)"),
2726 0);
2727 PyObjectPtr none(mainModuleGet("none"));
2728 ASSERT_EQ(none, Py_None);
2729} // namespace testing
2730
2731TEST_F(TypeExtensionApiTest, MemberStringRaisesTypeError) {
2732 auto verify_func = createBarTypeWithMembers();
2733 ASSERT_EQ(PyRun_SimpleString(R"(
2734b = Bar()
2735raised = False
2736try:
2737 b.t_string = "bar"
2738 raise RuntimeError("call didn't throw")
2739except TypeError:
2740 raised = True
2741r1 = b.t_string
2742)"),
2743 0);
2744 PyObjectPtr r1(mainModuleGet("r1"));
2745 PyObjectPtr raised(mainModuleGet("raised"));
2746 EXPECT_EQ(raised, Py_True);
2747 ASSERT_EQ(PyUnicode_Check(r1), 1);
2748 EXPECT_TRUE(isUnicodeEqualsCStr(r1, "foo"));
2749 PyObjectPtr b(mainModuleGet("b"));
2750 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2751}
2752
2753TEST_F(TypeExtensionApiTest, MemberObject) {
2754 auto verify_func = createBarTypeWithMembers();
2755 ASSERT_EQ(PyRun_SimpleString(R"(
2756b = Bar()
2757r1 = b.t_object
2758b.t_object = (1, "a", 2, "b", 3, "c")
2759r2 = b.t_object
2760b.t_object = r1
2761)"),
2762 0);
2763 PyObjectPtr r1(mainModuleGet("r1"));
2764 ASSERT_EQ(PyList_Check(r1), 1);
2765 EXPECT_EQ(PyList_Size(r1), 0);
2766 PyObjectPtr r2(mainModuleGet("r2"));
2767 ASSERT_EQ(PyTuple_Check(r2), 1);
2768 EXPECT_EQ(PyTuple_Size(r2), 6);
2769 PyObjectPtr b(mainModuleGet("b"));
2770 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2771}
2772
2773TEST_F(TypeExtensionApiTest, MemberObjectWithNull) {
2774 auto verify_func = createBarTypeWithMembers();
2775 ASSERT_EQ(PyRun_SimpleString(R"(
2776b = Bar()
2777r1 = b.t_object_null
2778)"),
2779 0);
2780 PyObjectPtr r1(mainModuleGet("r1"));
2781 EXPECT_EQ(r1, Py_None);
2782 PyObjectPtr b(mainModuleGet("b"));
2783 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2784}
2785
2786TEST_F(TypeExtensionApiTest, MemberObjectEx) {
2787 auto verify_func = createBarTypeWithMembers();
2788 ASSERT_EQ(PyRun_SimpleString(R"(
2789b = Bar()
2790r1 = b.t_objectex
2791b.t_objectex = tuple()
2792r2 = b.t_objectex
2793b.t_objectex = r1
2794)"),
2795 0);
2796 PyObjectPtr r1(mainModuleGet("r1"));
2797 ASSERT_EQ(PyList_Check(r1), 1);
2798 EXPECT_EQ(PyList_Size(r1), 0);
2799 PyObjectPtr r2(mainModuleGet("r2"));
2800 ASSERT_EQ(PyTuple_Check(r2), 1);
2801 EXPECT_EQ(PyTuple_Size(r2), 0);
2802 PyObjectPtr b(mainModuleGet("b"));
2803 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2804}
2805
2806TEST_F(TypeExtensionApiTest, MemberObjectExWithNullRaisesAttributeError) {
2807 auto verify_func = createBarTypeWithMembers();
2808 ASSERT_EQ(PyRun_SimpleString(R"(
2809b = Bar()
2810raised = False
2811try:
2812 b.t_objectex_null
2813 raise RuntimeError("call didn't throw")
2814except AttributeError:
2815 raised = True
2816)"),
2817 0);
2818 PyObjectPtr raised(mainModuleGet("raised"));
2819 EXPECT_EQ(raised, Py_True);
2820 PyObjectPtr b(mainModuleGet("b"));
2821 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2822}
2823
2824TEST_F(TypeExtensionApiTest, MemberPySsizeT) {
2825 auto verify_func = createBarTypeWithMembers();
2826 ASSERT_EQ(PyRun_SimpleString(R"(
2827b = Bar()
2828r1 = b.t_pyssize
2829b.t_pyssize = 4321
2830r2 = b.t_pyssize
2831b.t_pyssize = r1
2832)"),
2833 0);
2834 PyObjectPtr r1(mainModuleGet("r1"));
2835 ASSERT_EQ(PyLong_Check(r1), 1);
2836 EXPECT_EQ(PyLong_AsSsize_t(r1), 1234);
2837 PyObjectPtr r2(mainModuleGet("r2"));
2838 ASSERT_EQ(PyLong_Check(r2), 1);
2839 EXPECT_EQ(PyLong_AsSsize_t(r2), 4321);
2840 PyObjectPtr b(mainModuleGet("b"));
2841 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2842}
2843
2844TEST_F(TypeExtensionApiTest, MemberReadOnlyRaisesAttributeError) {
2845 auto verify_func = createBarTypeWithMembers();
2846 ASSERT_EQ(PyRun_SimpleString(R"(
2847b = Bar()
2848r1 = b.t_int_readonly
2849raised = False
2850try:
2851 b.t_int_readonly = 4321
2852 raise RuntimeError("call didn't throw")
2853except AttributeError:
2854 raised = True
2855)"),
2856 0);
2857 PyObjectPtr r1(mainModuleGet("r1"));
2858 ASSERT_EQ(PyLong_Check(r1), 1);
2859 EXPECT_TRUE(isLongEqualsLong(r1, -1234));
2860 PyObjectPtr raised(mainModuleGet("raised"));
2861 EXPECT_EQ(raised, Py_True);
2862 PyObjectPtr b(mainModuleGet("b"));
2863 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2864}
2865
2866TEST_F(TypeExtensionApiTest, MemberIntSetIncorrectTypeRaisesTypeError) {
2867 auto verify_func = createBarTypeWithMembers();
2868 ASSERT_EQ(PyRun_SimpleString(R"(
2869b = Bar()
2870raised = False
2871try:
2872 b.t_int = "foo"
2873 raise RuntimeError("call didn't throw")
2874except TypeError:
2875 raised = True
2876r1 = b.t_int
2877)"),
2878 0);
2879 PyObjectPtr r1(mainModuleGet("r1"));
2880 PyObjectPtr raised(mainModuleGet("raised"));
2881 EXPECT_EQ(raised, Py_True);
2882 ASSERT_EQ(PyLong_Check(r1), 1);
2883 EXPECT_TRUE(isLongEqualsLong(r1, -1234));
2884 PyObjectPtr b(mainModuleGet("b"));
2885 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2886}
2887
2888TEST_F(TypeExtensionApiTest, MemberCharIncorrectSizeRaisesTypeError) {
2889 auto verify_func = createBarTypeWithMembers();
2890 ASSERT_EQ(PyRun_SimpleString(R"(
2891b = Bar()
2892raised = False
2893try:
2894 b.t_char = "foo"
2895 raise RuntimeError("call didn't throw")
2896except TypeError:
2897 raised = True
2898r1 = b.t_char
2899)"),
2900 0);
2901 PyObjectPtr r1(mainModuleGet("r1"));
2902 PyObjectPtr raised(mainModuleGet("raised"));
2903 EXPECT_EQ(raised, Py_True);
2904 ASSERT_EQ(PyUnicode_Check(r1), 1);
2905 EXPECT_TRUE(isUnicodeEqualsCStr(r1, "a"));
2906 PyObjectPtr b(mainModuleGet("b"));
2907 ASSERT_NO_FATAL_FAILURE(verify_func(b));
2908}
2909
2910// Pyro raises a SystemError but CPython returns a new type.
2911// TODO(T56634824): Figure out why Pyro differs from CPython.
2912TEST_F(TypeExtensionApiTest, MemberUnknownRaisesSystemErrorPyro) {
2913 int unknown_type = -1;
2914 struct BarObject {
2915 PyObject_HEAD
2916 int value;
2917 };
2918 static const PyMemberDef members[] = {
2919 {"value", unknown_type, offsetof(BarObject, value), 0, nullptr},
2920 {nullptr},
2921 };
2922 static const PyType_Slot slots[] = {
2923 {Py_tp_members, const_cast<PyMemberDef*>(members)},
2924 {0, nullptr},
2925 };
2926 static PyType_Spec spec = {
2927 "__main__.Bar",
2928 sizeof(BarObject),
2929 0,
2930 Py_TPFLAGS_DEFAULT,
2931 const_cast<PyType_Slot*>(slots),
2932 };
2933 PyObjectPtr type(PyType_FromSpec(&spec));
2934 ASSERT_EQ(type, nullptr);
2935 ASSERT_NE(PyErr_Occurred(), nullptr);
2936 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
2937}
2938
2939static void createBarTypeWithGetSetObject() {
2940 struct BarObject {
2941 PyObject_HEAD
2942 long attribute;
2943 long readonly_attribute;
2944 };
2945
2946 getter attribute_getter = [](PyObject* self, void*) {
2947 return PyLong_FromLong(reinterpret_cast<BarObject*>(self)->attribute);
2948 };
2949
2950 setter attribute_setter = [](PyObject* self, PyObject* value, void*) {
2951 reinterpret_cast<BarObject*>(self)->attribute = PyLong_AsLong(value);
2952 return 0;
2953 };
2954
2955 getter readonly_attribute_getter = [](PyObject* self, void*) {
2956 return PyLong_FromLong(
2957 reinterpret_cast<BarObject*>(self)->readonly_attribute);
2958 };
2959
2960 setter raise_attribute_setter = [](PyObject*, PyObject*, void*) {
2961 PyErr_BadArgument();
2962 return -1;
2963 };
2964
2965 static PyGetSetDef getsets[4];
2966 getsets[0] = {"attribute", attribute_getter, attribute_setter};
2967 getsets[1] = {"readonly_attribute", readonly_attribute_getter, nullptr};
2968 getsets[2] = {"raise_attribute", attribute_getter, raise_attribute_setter};
2969 getsets[3] = {nullptr};
2970
2971 newfunc new_func = [](PyTypeObject* type, PyObject*, PyObject*) {
2972 void* slot = PyType_GetSlot(type, Py_tp_alloc);
2973 return reinterpret_cast<allocfunc>(slot)(type, 0);
2974 };
2975 initproc init_func = [](PyObject* self, PyObject*, PyObject*) {
2976 reinterpret_cast<BarObject*>(self)->attribute = 123;
2977 reinterpret_cast<BarObject*>(self)->readonly_attribute = 456;
2978 return 0;
2979 };
2980 static PyType_Slot slots[6];
2981 // TODO(T40540469): Most of functions should be inherited from object.
2982 // However, inheritance is not supported yet. For now, just set them manually.
2983 slots[0] = {Py_tp_new, reinterpret_cast<void*>(new_func)};
2984 slots[1] = {Py_tp_init, reinterpret_cast<void*>(init_func)};
2985 slots[2] = {Py_tp_alloc, reinterpret_cast<void*>(PyType_GenericAlloc)},
2986 slots[3] = {Py_tp_dealloc, reinterpret_cast<void*>(deallocLeafObject)};
2987 slots[4] = {Py_tp_getset, reinterpret_cast<void*>(getsets)};
2988 slots[5] = {0, nullptr};
2989 static PyType_Spec spec;
2990 spec = {
2991 "__main__.Bar", sizeof(BarObject), 0, Py_TPFLAGS_DEFAULT, slots,
2992 };
2993 PyObjectPtr type(PyType_FromSpec(&spec));
2994 ASSERT_NE(type, nullptr);
2995 ASSERT_EQ(PyType_CheckExact(type), 1);
2996 ASSERT_EQ(moduleSet("__main__", "Bar", type), 0);
2997}
2998
2999TEST_F(TypeExtensionApiTest, GetSetAttributePyro) {
3000 ASSERT_NO_FATAL_FAILURE(createBarTypeWithGetSetObject());
3001 ASSERT_EQ(PyRun_SimpleString(R"(
3002b = Bar()
3003r1 = b.attribute
3004b.attribute = 321
3005r2 = b.attribute
3006)"),
3007 0);
3008 PyObjectPtr r1(mainModuleGet("r1"));
3009 ASSERT_EQ(PyLong_Check(r1), 1);
3010 EXPECT_EQ(PyLong_AsLong(r1), 123);
3011 PyObjectPtr r2(mainModuleGet("r2"));
3012 ASSERT_EQ(PyLong_Check(r2), 1);
3013 EXPECT_EQ(PyLong_AsLong(r2), 321);
3014}
3015
3016TEST_F(TypeExtensionApiTest, GetSetReadonlyAttributePyro) {
3017 ASSERT_NO_FATAL_FAILURE(createBarTypeWithGetSetObject());
3018 ASSERT_EQ(PyRun_SimpleString(R"(
3019b = Bar()
3020raised = False
3021try:
3022 b.readonly_attribute = 321
3023 raise RuntimeError("call didn't throw")
3024except AttributeError:
3025 raised = True
3026r1 = b.readonly_attribute
3027)"),
3028 0);
3029 PyObjectPtr r1(mainModuleGet("r1"));
3030 PyObjectPtr raised(mainModuleGet("raised"));
3031 EXPECT_EQ(raised, Py_True);
3032 ASSERT_EQ(PyLong_Check(r1), 1);
3033 EXPECT_EQ(PyLong_AsLong(r1), 456);
3034}
3035
3036TEST_F(TypeExtensionApiTest, GetSetRaiseAttributePyro) {
3037 ASSERT_NO_FATAL_FAILURE(createBarTypeWithGetSetObject());
3038 ASSERT_EQ(PyRun_SimpleString(R"(
3039b = Bar()
3040raised = False
3041try:
3042 b.raise_attribute = 321
3043 raise SystemError("call didn't throw")
3044except TypeError:
3045 raised = True
3046r1 = b.raise_attribute
3047)"),
3048 0);
3049 PyObjectPtr r1(mainModuleGet("r1"));
3050 PyObjectPtr raised(mainModuleGet("raised"));
3051 EXPECT_EQ(raised, Py_True);
3052 ASSERT_EQ(PyLong_Check(r1), 1);
3053 EXPECT_EQ(PyLong_AsLong(r1), 123);
3054}
3055
3056TEST_F(TypeExtensionApiTest, PyTypeNameWithNullTypeRaisesSystemErrorPyro) {
3057 EXPECT_EQ(_PyType_Name(nullptr), nullptr);
3058 ASSERT_NE(PyErr_Occurred(), nullptr);
3059 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
3060}
3061
3062TEST_F(TypeExtensionApiTest, PyTypeNameWithNonTypeRaisesSystemErrorPyro) {
3063 PyObjectPtr long_obj(PyLong_FromLong(5));
3064 EXPECT_EQ(_PyType_Name(reinterpret_cast<PyTypeObject*>(long_obj.get())),
3065 nullptr);
3066 ASSERT_NE(PyErr_Occurred(), nullptr);
3067 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
3068}
3069
3070TEST_F(TypeExtensionApiTest, PyTypeNameWithBuiltinTypeReturnsName) {
3071 PyObjectPtr long_obj(PyLong_FromLong(5));
3072 const char* name = _PyType_Name(Py_TYPE(long_obj));
3073 EXPECT_STREQ(name, "int");
3074}
3075
3076TEST_F(TypeExtensionApiTest, PyTypeNameReturnsSamePointerEachCall) {
3077 PyObjectPtr long_obj(PyLong_FromLong(5));
3078 const char* name = _PyType_Name(Py_TYPE(long_obj));
3079 EXPECT_STREQ(name, "int");
3080 const char* name2 = _PyType_Name(Py_TYPE(long_obj));
3081 EXPECT_EQ(name, name2);
3082}
3083
3084TEST_F(TypeExtensionApiTest, PyTypeNameWithUserDefinedTypeReturnsName) {
3085 PyRun_SimpleString(R"(
3086class FooBarTheBaz:
3087 pass
3088)");
3089 PyObjectPtr c(mainModuleGet("FooBarTheBaz"));
3090 const char* name = _PyType_Name(reinterpret_cast<PyTypeObject*>(c.get()));
3091 EXPECT_STREQ(name, "FooBarTheBaz");
3092}
3093
3094static PyObject* emptyBinaryFunc(PyObject*, PyObject*) { return Py_None; }
3095
3096static void emptyDestructorFunc(PyObject*) { return; }
3097
3098static Py_ssize_t emptyLenFunc(PyObject*) { return Py_ssize_t{0}; }
3099
3100static PyObject* emptyCompareFunc(PyObject*, PyObject*, int) { return Py_None; }
3101
3102static int emptySetattroFunc(PyObject*, PyObject*, PyObject*) { return 0; }
3103
3104static PyObject* emptyTernaryFunc(PyObject*, PyObject*, PyObject*) {
3105 return Py_None;
3106}
3107
3108static PyObject* emptyUnaryFunc(PyObject*) { return Py_None; }
3109
3110TEST_F(TypeExtensionApiTest,
3111 GetSlotFromExceptionWithTpNewReturnsConstructorPyro) {
3112 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3113 "Subclass", Py_tp_init, &emptyUnaryFunc, PyExc_Exception));
3114 PyObjectPtr subclass(mainModuleGet("Subclass"));
3115 ASSERT_NE(subclass, nullptr);
3116 newfunc dunder_new = reinterpret_cast<newfunc>(PyType_GetSlot(
3117 reinterpret_cast<PyTypeObject*>(PyExc_Exception), Py_tp_new));
3118 ASSERT_EQ(PyErr_Occurred(), nullptr);
3119 ASSERT_NE(dunder_new, nullptr);
3120 PyObjectPtr instance(dunder_new(
3121 reinterpret_cast<PyTypeObject*>(subclass.get()), nullptr, nullptr));
3122 ASSERT_NE(instance, nullptr);
3123 EXPECT_TRUE(PyErr_GivenExceptionMatches(instance, subclass));
3124}
3125
3126TEST_F(TypeExtensionApiTest,
3127 GetSlotFromExceptionWithNonZeroSizeWithTpNewReturnsConstructorPyro) {
3128 PyType_Slot slots[] = {
3129 {0, nullptr},
3130 };
3131 static PyType_Spec spec;
3132 spec = {
3133 "foo.Foo", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
3134 slots,
3135 };
3136 PyObjectPtr bases(PyTuple_Pack(1, PyExc_Exception));
3137 PyObjectPtr subclass(PyType_FromSpecWithBases(&spec, bases));
3138
3139 ASSERT_NE(subclass, nullptr);
3140 newfunc dunder_new = reinterpret_cast<newfunc>(PyType_GetSlot(
3141 reinterpret_cast<PyTypeObject*>(PyExc_Exception), Py_tp_new));
3142 ASSERT_EQ(PyErr_Occurred(), nullptr);
3143 ASSERT_NE(dunder_new, nullptr);
3144 PyObjectPtr instance(dunder_new(
3145 reinterpret_cast<PyTypeObject*>(subclass.get()), nullptr, nullptr));
3146 ASSERT_NE(instance, nullptr);
3147 EXPECT_TRUE(PyErr_GivenExceptionMatches(instance, subclass));
3148}
3149
3150TEST_F(TypeExtensionApiTest,
3151 ExceptionSubclassWithNonZeroConstructorCreatesExceptionSubclass) {
3152 PyObjectPtr basicsize(
3153 PyObject_GetAttrString(PyExc_Exception, "__basicsize__"));
3154 ASSERT_TRUE(PyLong_Check(basicsize));
3155 int size = _PyLong_AsInt(basicsize);
3156
3157 PyType_Slot slots[] = {
3158 {0, nullptr},
3159 };
3160 static PyType_Spec spec;
3161 spec = {
3162 "foo.Foo", size, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
3163 };
3164 PyObjectPtr bases(PyTuple_Pack(1, PyExc_Exception));
3165 PyObjectPtr subclass(PyType_FromSpecWithBases(&spec, bases));
3166
3167 ASSERT_NE(subclass, nullptr);
3168 newfunc dunder_new = reinterpret_cast<newfunc>(PyType_GetSlot(
3169 reinterpret_cast<PyTypeObject*>(subclass.get()), Py_tp_new));
3170 ASSERT_EQ(PyErr_Occurred(), nullptr);
3171 ASSERT_NE(dunder_new, nullptr);
3172 PyObjectPtr empty_tuple(PyTuple_New(0));
3173 PyObjectPtr instance(dunder_new(
3174 reinterpret_cast<PyTypeObject*>(subclass.get()), empty_tuple, nullptr));
3175 ASSERT_NE(instance, nullptr);
3176 EXPECT_TRUE(PyErr_GivenExceptionMatches(instance, subclass));
3177}
3178
3179TEST_F(TypeExtensionApiTest, GetSlotFromTypeWithTpNewReturnsConstructorPyro) {
3180 ASSERT_NO_FATAL_FAILURE(
3181 createTypeWithSlotAndBase("Subclass", Py_tp_init, &emptyUnaryFunc,
3182 reinterpret_cast<PyObject*>(&PyType_Type)));
3183 PyObjectPtr subclass(mainModuleGet("Subclass"));
3184 ASSERT_NE(subclass, nullptr);
3185 newfunc dunder_new =
3186 reinterpret_cast<newfunc>(PyType_GetSlot(&PyType_Type, Py_tp_new));
3187 ASSERT_EQ(PyErr_Occurred(), nullptr);
3188 ASSERT_NE(dunder_new, nullptr);
3189
3190 PyObjectPtr args(PyTuple_New(3));
3191 ASSERT_EQ(PyTuple_SetItem(args, 0, PyUnicode_FromString("Subclass")), 0);
3192 ASSERT_EQ(PyTuple_SetItem(args, 1, PyTuple_Pack(1, &PyType_Type)), 0);
3193 ASSERT_EQ(PyTuple_SetItem(args, 2, PyDict_New()), 0);
3194
3195 PyObjectPtr instance(dunder_new(
3196 reinterpret_cast<PyTypeObject*>(subclass.get()), args, nullptr));
3197 ASSERT_NE(instance, nullptr);
3198 EXPECT_TRUE(PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(instance.get()),
3199 &PyType_Type));
3200}
3201
3202TEST_F(TypeExtensionApiTest, GetDestructorSlotsFromExceptionReturnsNoOpsPyro) {
3203 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3204 "Subclass", Py_tp_new, &emptyBinaryFunc, PyExc_Exception));
3205 PyObject* subclass = mainModuleGet("Subclass");
3206 PyTypeObject* exception = reinterpret_cast<PyTypeObject*>(PyExc_Exception);
3207
3208 inquiry tp_clear =
3209 reinterpret_cast<inquiry>(PyType_GetSlot(exception, Py_tp_clear));
3210 ASSERT_EQ(PyErr_Occurred(), nullptr);
3211 ASSERT_NE(tp_clear, nullptr);
3212 EXPECT_EQ(tp_clear(subclass), 0);
3213
3214 destructor tp_dealloc =
3215 reinterpret_cast<destructor>(PyType_GetSlot(exception, Py_tp_dealloc));
3216 ASSERT_EQ(PyErr_Occurred(), nullptr);
3217 ASSERT_NE(tp_dealloc, nullptr);
3218 tp_dealloc(subclass);
3219 ASSERT_EQ(PyErr_Occurred(), nullptr);
3220
3221 traverseproc tp_traverse =
3222 reinterpret_cast<traverseproc>(PyType_GetSlot(exception, Py_tp_traverse));
3223 ASSERT_EQ(PyErr_Occurred(), nullptr);
3224 ASSERT_NE(tp_traverse, nullptr);
3225 EXPECT_EQ(tp_traverse(
3226 subclass, [](PyObject*, void*) { return 0; }, nullptr),
3227 0);
3228}
3229
3230TEST_F(TypeExtensionApiTest, FromSpecWithBasesSetsBaseSlots) {
3231 ASSERT_NO_FATAL_FAILURE(
3232 createTypeWithSlot("BaseType", Py_nb_add, &emptyBinaryFunc));
3233 PyObjectPtr base_type(mainModuleGet("BaseType"));
3234 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3235 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3236 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3237
3238 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3239 PyObject* bases = static_cast<PyObject*>(PyType_GetSlot(tp, Py_tp_bases));
3240 ASSERT_NE(bases, nullptr);
3241 EXPECT_EQ(PyTuple_Check(bases), 1);
3242 EXPECT_EQ(PyTuple_Size(bases), 1);
3243 EXPECT_EQ(static_cast<PyObject*>(PyType_GetSlot(tp, Py_tp_base)),
3244 base_type.get());
3245}
3246
3247TEST_F(TypeExtensionApiTest, FromSpecWithBasesWithBuiltinBase) {
3248 static PyType_Slot slots[] = {
3249 {0, nullptr},
3250 };
3251 static PyType_Spec spec;
3252 spec = {
3253 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
3254 };
3255 PyObjectPtr bases(PyTuple_Pack(1, &PyType_Type));
3256 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3257 ASSERT_NE(type.get(), nullptr);
3258 ASSERT_TRUE(PyType_CheckExact(type.get()));
3259 EXPECT_TRUE(PyObject_IsInstance(reinterpret_cast<PyObject*>(type.get()),
3260 reinterpret_cast<PyObject*>(&PyType_Type)));
3261
3262 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3263 PyObject* tp_bases = static_cast<PyObject*>(PyType_GetSlot(tp, Py_tp_bases));
3264 ASSERT_NE(tp_bases, nullptr);
3265 EXPECT_EQ(PyTuple_Check(tp_bases), 1);
3266 EXPECT_EQ(PyTuple_Size(tp_bases), 1);
3267 EXPECT_EQ(static_cast<PyTypeObject*>(PyType_GetSlot(tp, Py_tp_base)),
3268 &PyType_Type);
3269}
3270
3271TEST_F(TypeExtensionApiTest,
3272 FromSpecWithBasesWithTypeObjectAsBaseInheritsTpSetAttro) {
3273 static PyType_Slot slots[] = {
3274 {0, nullptr},
3275 };
3276 static PyType_Spec spec;
3277 spec = {
3278 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
3279 };
3280 PyObjectPtr bases(PyTuple_Pack(1, &PyType_Type));
3281 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3282 ASSERT_NE(type.get(), nullptr);
3283 ASSERT_TRUE(PyType_CheckExact(type.get()));
3284 EXPECT_TRUE(PyObject_IsInstance(reinterpret_cast<PyObject*>(type.get()),
3285 reinterpret_cast<PyObject*>(&PyType_Type)));
3286
3287 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3288 ASSERT_NE(PyType_GetSlot(tp, Py_tp_setattro), nullptr);
3289 setattrofunc set_attrofunc =
3290 reinterpret_cast<setattrofunc>(PyType_GetSlot(tp, Py_tp_setattro));
3291 PyObjectPtr name(PyUnicode_FromString("foo"));
3292 PyObjectPtr value(PyUnicode_FromString("foo_value"));
3293 EXPECT_EQ(set_attrofunc(type.get(), name.get(), value.get()), 0);
3294
3295 PyObjectPtr foo_value(PyObject_GetAttrString(type.get(), "foo"));
3296 EXPECT_EQ(value.get(), foo_value.get());
3297
3298 PyObjectPtr non_str_name(PyLong_FromLong(10));
3299 EXPECT_NE(set_attrofunc(type.get(), non_str_name.get(), value.get()), 0);
3300 ASSERT_NE(PyErr_Occurred(), nullptr);
3301 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
3302 PyErr_Clear();
3303}
3304
3305TEST_F(TypeExtensionApiTest, FromSpecWithBasesWithoutBasetypeIsRejectedAsBase) {
3306 static PyType_Slot slots[] = {
3307 {0, nullptr},
3308 };
3309 static PyType_Spec spec;
3310 spec = {
3311 "Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
3312 };
3313 PyObjectPtr bases(PyTuple_Pack(1, &PyType_Type));
3314 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3315 ASSERT_NE(type.get(), nullptr);
3316 PyObjectPtr main(Borrowed(PyImport_AddModule("__main__")));
3317 Py_INCREF(type.get());
3318 PyModule_AddObject(main, "Bar", type);
3319 PyObjectPtr main_dict(Borrowed(PyModule_GetDict(main)));
3320 EXPECT_EQ(
3321 PyRun_String("class C(Bar): pass", Py_file_input, main_dict, main_dict),
3322 nullptr);
3323 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
3324 PyObject* exc_type;
3325 PyObject* exc_value;
3326 PyObject* exc_traceback;
3327 PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
3328 EXPECT_TRUE(isUnicodeEqualsCStr(exc_value,
3329 "type 'Bar' is not an acceptable base type"));
3330}
3331
3332TEST_F(
3333 TypeExtensionApiTest,
3334 FromSpecWithBasesWithNonZeroSizeBaseAndZeroBasicSizeAndItemSetsCustomTpNewPyro) {
3335 PyType_Slot base_slots[] = {
3336 {0, nullptr},
3337 };
3338 static PyType_Spec base_spec;
3339 base_spec = {
3340 "foo.Foo", 16, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, base_slots,
3341 };
3342 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3343
3344 PyType_Slot slots[] = {
3345 {0, nullptr},
3346 };
3347 static PyType_Spec spec;
3348 spec = {
3349 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
3350 };
3351 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3352 PyObjectPtr ext_type(PyType_FromSpecWithBases(&spec, bases));
3353 ASSERT_NE(ext_type, nullptr);
3354 ASSERT_TRUE(PyType_CheckExact(ext_type));
3355
3356 PyRun_SimpleString(R"(class D: pass)");
3357 PyObjectPtr managed_type(mainModuleGet("D"));
3358 ASSERT_NE(PyType_GetSlot(managed_type.asTypeObject(), Py_tp_new), nullptr);
3359 EXPECT_NE(PyType_GetSlot(managed_type.asTypeObject(), Py_tp_new),
3360 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
3361
3362 // Instances of `ext_type` does not cause any memory leak.
3363 auto new_slot = reinterpret_cast<newfunc>(
3364 PyType_GetSlot(ext_type.asTypeObject(), Py_tp_new));
3365 PyObjectPtr args(PyTuple_New(0));
3366 PyObjectPtr kwargs(PyDict_New());
3367 PyObjectPtr result(new_slot(ext_type.asTypeObject(), args, kwargs));
3368 ASSERT_NE(result, nullptr);
3369 EXPECT_EQ(PyObject_IsInstance(result, ext_type), 1);
3370}
3371
3372TEST_F(TypeExtensionApiTest,
3373 FromSpecWithBasesWithoutBaseTypeFlagsRaisesTypeError) {
3374 static PyType_Slot base_slots[1];
3375 base_slots[0] = {0, nullptr};
3376 static PyType_Spec base_spec;
3377 base_spec = {
3378 "__main__.BaseType", 0, 0, Py_TPFLAGS_DEFAULT, base_slots,
3379 };
3380 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3381 ASSERT_NE(base_type, nullptr);
3382 ASSERT_EQ(PyType_CheckExact(base_type), 1);
3383
3384 static PyType_Slot slots[1];
3385 slots[0] = {0, nullptr};
3386 static PyType_Spec spec;
3387 spec = {
3388 "__main__.SubclassedType", 0, 0, Py_TPFLAGS_DEFAULT, slots,
3389 };
3390 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3391 ASSERT_EQ(PyType_FromSpecWithBases(&spec, bases), nullptr);
3392 ASSERT_NE(PyErr_Occurred(), nullptr);
3393 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
3394}
3395
3396TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsNumberSlots) {
3397 binaryfunc empty_binary_func2 = [](PyObject*, PyObject*) { return Py_None; };
3398 ASSERT_NO_FATAL_FAILURE(
3399 createTypeWithSlot("BaseType", Py_nb_add, &emptyBinaryFunc));
3400 PyObjectPtr base_type(mainModuleGet("BaseType"));
3401 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3402 "SubclassedType", Py_nb_subtract, empty_binary_func2, base_type));
3403 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3404
3405 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3406 EXPECT_EQ(reinterpret_cast<binaryfunc>(PyType_GetSlot(tp, Py_nb_add)),
3407 &emptyBinaryFunc);
3408 EXPECT_EQ(reinterpret_cast<binaryfunc>(PyType_GetSlot(tp, Py_nb_subtract)),
3409 empty_binary_func2);
3410}
3411
3412TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsAsyncSlots) {
3413 unaryfunc empty_unary_func2 = [](PyObject*) { return Py_None; };
3414 ASSERT_NO_FATAL_FAILURE(
3415 createTypeWithSlot("BaseType", Py_am_await, &emptyUnaryFunc));
3416 PyObjectPtr base_type(mainModuleGet("BaseType"));
3417 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3418 "SubclassedType", Py_am_aiter, empty_unary_func2, base_type));
3419 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3420
3421 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3422 EXPECT_EQ(reinterpret_cast<unaryfunc>(PyType_GetSlot(tp, Py_am_await)),
3423 &emptyUnaryFunc);
3424 EXPECT_EQ(reinterpret_cast<unaryfunc>(PyType_GetSlot(tp, Py_am_aiter)),
3425 empty_unary_func2);
3426}
3427
3428TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsSequenceSlots) {
3429 ssizeargfunc empty_sizearg_func = [](PyObject*, Py_ssize_t) {
3430 return Py_None;
3431 };
3432 ASSERT_NO_FATAL_FAILURE(
3433 createTypeWithSlot("BaseType", Py_sq_concat, &emptyBinaryFunc));
3434 PyObjectPtr base_type(mainModuleGet("BaseType"));
3435 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3436 "SubclassedType", Py_sq_repeat, empty_sizearg_func, base_type));
3437 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3438
3439 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3440 EXPECT_EQ(reinterpret_cast<binaryfunc>(PyType_GetSlot(tp, Py_sq_concat)),
3441 &emptyBinaryFunc);
3442 EXPECT_EQ(reinterpret_cast<ssizeargfunc>(PyType_GetSlot(tp, Py_sq_repeat)),
3443 empty_sizearg_func);
3444}
3445
3446TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsMappingSlots) {
3447 ASSERT_NO_FATAL_FAILURE(
3448 createTypeWithSlot("BaseType", Py_mp_subscript, &emptyBinaryFunc));
3449 PyObjectPtr base_type(mainModuleGet("BaseType"));
3450 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3451 "SubclassedType", Py_mp_length, &emptyLenFunc, base_type));
3452 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3453
3454 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3455 EXPECT_EQ(reinterpret_cast<binaryfunc>(PyType_GetSlot(tp, Py_mp_subscript)),
3456 &emptyBinaryFunc);
3457 EXPECT_EQ(reinterpret_cast<lenfunc>(PyType_GetSlot(tp, Py_mp_length)),
3458 &emptyLenFunc);
3459}
3460
3461TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsTypeSlots) {
3462 ASSERT_NO_FATAL_FAILURE(
3463 createTypeWithSlot("BaseType", Py_tp_call, &emptyTernaryFunc));
3464 PyObjectPtr base_type(mainModuleGet("BaseType"));
3465 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3466 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3467 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3468
3469 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3470 EXPECT_EQ(reinterpret_cast<ternaryfunc>(PyType_GetSlot(tp, Py_tp_call)),
3471 &emptyTernaryFunc);
3472}
3473
3474TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsMixedSlots) {
3475 ASSERT_NO_FATAL_FAILURE(
3476 createTypeWithSlot("BaseType", Py_nb_add, &emptyBinaryFunc));
3477 PyObjectPtr base_type(mainModuleGet("BaseType"));
3478 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3479 "SubclassedType", Py_mp_length, &emptyLenFunc, base_type));
3480 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3481
3482 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3483 EXPECT_EQ(reinterpret_cast<binaryfunc>(PyType_GetSlot(tp, Py_nb_add)),
3484 &emptyBinaryFunc);
3485 EXPECT_EQ(reinterpret_cast<lenfunc>(PyType_GetSlot(tp, Py_mp_length)),
3486 &emptyLenFunc);
3487}
3488
3489TEST_F(TypeExtensionApiTest, FromSpecWithBasesDoesNotInheritGetAttrIfDefined) {
3490 getattrfunc empty_getattr_func = [](PyObject*, char*) { return Py_None; };
3491 ASSERT_NO_FATAL_FAILURE(
3492 createTypeWithSlot("BaseType", Py_tp_getattro, &emptyBinaryFunc));
3493 PyObjectPtr base_type(mainModuleGet("BaseType"));
3494 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3495 "SubclassedType", Py_tp_getattr, empty_getattr_func, base_type));
3496 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3497
3498 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3499 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_getattro), nullptr);
3500 EXPECT_EQ(reinterpret_cast<getattrfunc>(PyType_GetSlot(tp, Py_tp_getattr)),
3501 empty_getattr_func);
3502}
3503
3504TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsGetAttrIfNotDefined) {
3505 ASSERT_NO_FATAL_FAILURE(
3506 createTypeWithSlot("BaseType", Py_tp_getattro, &emptyBinaryFunc));
3507 PyObjectPtr base_type(mainModuleGet("BaseType"));
3508 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3509 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3510 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3511
3512 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3513 EXPECT_EQ(reinterpret_cast<getattrofunc>(PyType_GetSlot(tp, Py_tp_getattro)),
3514 &emptyBinaryFunc);
3515 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_getattr), nullptr);
3516}
3517
3518TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsSetAttrIfNotDefined) {
3519 ASSERT_NO_FATAL_FAILURE(
3520 createTypeWithSlot("BaseType", Py_tp_setattro, &emptySetattroFunc));
3521 PyObjectPtr base_type(mainModuleGet("BaseType"));
3522 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3523 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3524 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3525
3526 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3527 EXPECT_EQ(reinterpret_cast<setattrofunc>(PyType_GetSlot(tp, Py_tp_setattro)),
3528 &emptySetattroFunc);
3529 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_setattr), nullptr);
3530}
3531
3532TEST_F(TypeExtensionApiTest,
3533 FromSpecWithBasesDoesNotInheritCompareAndHashIfDefined) {
3534 hashfunc empty_hash_func = [](PyObject*) { return Py_hash_t{0}; };
3535 ASSERT_NO_FATAL_FAILURE(
3536 createTypeWithSlot("BaseType", Py_tp_richcompare, &emptyCompareFunc));
3537 PyObjectPtr base_type(mainModuleGet("BaseType"));
3538 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3539 "SubclassedType", Py_tp_hash, empty_hash_func, base_type));
3540 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3541
3542 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3543 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_richcompare), nullptr);
3544 EXPECT_EQ(reinterpret_cast<hashfunc>(PyType_GetSlot(tp, Py_tp_hash)),
3545 empty_hash_func);
3546}
3547
3548TEST_F(TypeExtensionApiTest,
3549 FromSpecWithBasesInheritsCompareAndHashIfNotDefined) {
3550 ASSERT_NO_FATAL_FAILURE(
3551 createTypeWithSlot("BaseType", Py_tp_richcompare, &emptyCompareFunc));
3552 PyObjectPtr base_type(mainModuleGet("BaseType"));
3553 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3554 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3555 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3556
3557 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3558 EXPECT_EQ(
3559 reinterpret_cast<richcmpfunc>(PyType_GetSlot(tp, Py_tp_richcompare)),
3560 &emptyCompareFunc);
3561}
3562
3563TEST_F(TypeExtensionApiTest,
3564 FromSpecWithBasesInheritsFinalizeRegardlessOfFlag) {
3565 static PyType_Slot base_slots[2];
3566 base_slots[0] = {Py_tp_finalize,
3567 reinterpret_cast<void*>(&emptyDestructorFunc)};
3568 base_slots[1] = {0, nullptr};
3569 static PyType_Spec base_spec;
3570 base_spec = {
3571 "__main__.BaseType",
3572 0,
3573 0,
3574 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE,
3575 base_slots,
3576 };
3577 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3578 ASSERT_NE(base_type, nullptr);
3579 ASSERT_EQ(PyType_CheckExact(base_type), 1);
3580
3581 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3582 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3583 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3584
3585 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3586 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_finalize),
3587 reinterpret_cast<void*>(&emptyDestructorFunc));
3588}
3589
3590TEST_F(TypeExtensionApiTest,
3591 FromSpecWithBasesInheritsFinalizeWhenWhateverFlagSet) {
3592 static PyType_Slot base_slots[2];
3593 base_slots[0] = {Py_tp_finalize,
3594 reinterpret_cast<void*>(&emptyDestructorFunc)};
3595 base_slots[1] = {0, nullptr};
3596 static PyType_Spec base_spec;
3597 base_spec = {
3598 "__main__.BaseType",
3599 0,
3600 0,
3601 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE,
3602 base_slots,
3603 };
3604 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3605 ASSERT_NE(base_type, nullptr);
3606 ASSERT_EQ(PyType_CheckExact(base_type), 1);
3607
3608 static PyType_Slot slots[1];
3609 slots[0] = {0, nullptr};
3610 static PyType_Spec spec;
3611 spec = {
3612 "__main__.SubclassedType",
3613 0,
3614 0,
3615 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE,
3616 slots,
3617 };
3618 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3619 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3620 ASSERT_NE(type, nullptr);
3621 ASSERT_EQ(PyType_CheckExact(type), 1);
3622
3623 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3624 EXPECT_EQ(reinterpret_cast<destructor>(PyType_GetSlot(tp, Py_tp_finalize)),
3625 &emptyDestructorFunc);
3626}
3627
3628TEST_F(TypeExtensionApiTest,
3629 FromSpecWithBasesDoesNotInheritFreeIfHaveGCUnsetInBase) {
3630 freefunc empty_free_func = [](void*) { return; };
3631 ASSERT_NO_FATAL_FAILURE(
3632 createTypeWithSlot("BaseType", Py_tp_free, empty_free_func));
3633 PyObjectPtr base_type(mainModuleGet("BaseType"));
3634
3635 static PyType_Slot slots[1];
3636 slots[0] = {0, nullptr};
3637 static PyType_Spec spec;
3638 spec = {
3639 "__main__.SubclassedType",
3640 0,
3641 0,
3642 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
3643 slots,
3644 };
3645 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3646 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3647 ASSERT_NE(type, nullptr);
3648 ASSERT_EQ(PyType_CheckExact(type), 1);
3649
3650 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3651 EXPECT_NE(reinterpret_cast<freefunc>(PyType_GetSlot(tp, Py_tp_free)),
3652 empty_free_func);
3653}
3654
3655TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsFreeIfBothHaveGCFlagSet) {
3656 freefunc empty_free_func = PyObject_Free;
3657 ASSERT_NO_FATAL_FAILURE(
3658 createTypeWithSlot("BaseType", Py_tp_free, empty_free_func));
3659 PyObjectPtr base_type(mainModuleGet("BaseType"));
3660
3661 static PyType_Slot slots[1];
3662 slots[0] = {0, nullptr};
3663 static PyType_Spec spec;
3664 spec = {
3665 "__main__.SubclassedType",
3666 0,
3667 0,
3668 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
3669 slots,
3670 };
3671 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3672 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3673 ASSERT_NE(type, nullptr);
3674 ASSERT_EQ(PyType_CheckExact(type), 1);
3675
3676 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3677 EXPECT_EQ(PyType_GetSlot(tp, Py_tp_free),
3678 reinterpret_cast<void*>(PyObject_GC_Del));
3679}
3680
3681TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsIfGcFlagIsPresentOnBoth) {
3682 freefunc empty_free_func = [](void*) { return; };
3683 static PyType_Slot base_slots[2];
3684 base_slots[0] = {Py_tp_free, reinterpret_cast<void*>(empty_free_func)};
3685 base_slots[1] = {0, nullptr};
3686 static PyType_Spec base_spec;
3687 base_spec = {
3688 "__main__.BaseType",
3689 0,
3690 0,
3691 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
3692 base_slots,
3693 };
3694 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3695 ASSERT_NE(base_type, nullptr);
3696 ASSERT_EQ(PyType_CheckExact(base_type), 1);
3697
3698 static PyType_Slot slots[1];
3699 slots[0] = {0, nullptr};
3700 static PyType_Spec spec;
3701 spec = {
3702 "__main__.SubclassedType",
3703 0,
3704 0,
3705 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
3706 slots,
3707 };
3708 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3709 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3710 ASSERT_NE(type, nullptr);
3711 ASSERT_EQ(PyType_CheckExact(type), 1);
3712
3713 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3714 EXPECT_EQ(reinterpret_cast<freefunc>(PyType_GetSlot(tp, Py_tp_free)),
3715 empty_free_func);
3716}
3717
3718TEST_F(TypeExtensionApiTest, FromSpecWithBasesPopulatesTpDeallocIfNotDefined) {
3719 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3720 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3721 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3722 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3723 auto dealloc_func =
3724 reinterpret_cast<destructor>(PyType_GetSlot(tp, Py_tp_dealloc));
3725 EXPECT_NE(dealloc_func, nullptr);
3726}
3727
3728TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsObjectReprIfNotDefined) {
3729 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3730 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3731 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3732 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3733 auto repr_func = reinterpret_cast<reprfunc>(PyType_GetSlot(tp, Py_tp_repr));
3734 ASSERT_NE(repr_func, nullptr);
3735
3736 PyObjectPtr instance(_PyObject_CallNoArg(subclassed_type));
3737 PyObjectPtr slot_result(repr_func(instance));
3738 ASSERT_EQ(PyErr_Occurred(), nullptr);
3739 PyObjectPtr repr_result(PyObject_Repr(instance));
3740 ASSERT_EQ(PyErr_Occurred(), nullptr);
3741 EXPECT_EQ(PyUnicode_Compare(slot_result, repr_result), 0);
3742}
3743
3744TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsObjectStrIfNotDefined) {
3745 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3746 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3747 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3748 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3749 auto str_func = reinterpret_cast<reprfunc>(PyType_GetSlot(tp, Py_tp_str));
3750 ASSERT_NE(str_func, nullptr);
3751
3752 PyObjectPtr instance(_PyObject_CallNoArg(subclassed_type));
3753 PyObjectPtr slot_result(str_func(instance));
3754 ASSERT_EQ(PyErr_Occurred(), nullptr);
3755 PyObjectPtr str_result(PyObject_Str(instance));
3756 ASSERT_EQ(PyErr_Occurred(), nullptr);
3757 EXPECT_EQ(PyUnicode_Compare(slot_result, str_result), 0);
3758}
3759
3760TEST_F(TypeExtensionApiTest, FromSpecWithBasesPopulatesTpInitIfNotDefined) {
3761 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3762 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3763 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3764 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3765 auto init_proc = reinterpret_cast<initproc>(PyType_GetSlot(tp, Py_tp_init));
3766 ASSERT_NE(init_proc, nullptr);
3767}
3768
3769TEST_F(TypeExtensionApiTest,
3770 FromSpecWithBasesInheritsPyTypeGenericAllocIfNotDefined) {
3771 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3772 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3773 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3774 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3775 auto alloc_func =
3776 reinterpret_cast<allocfunc>(PyType_GetSlot(tp, Py_tp_alloc));
3777 EXPECT_EQ(alloc_func, &PyType_GenericAlloc);
3778}
3779
3780TEST_F(TypeExtensionApiTest, FromSpecWithBasesPopulatesTpNewIfNotDefined) {
3781 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3782 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3783 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3784 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3785 auto new_func = reinterpret_cast<newfunc>(PyType_GetSlot(tp, Py_tp_init));
3786 EXPECT_NE(new_func, nullptr);
3787}
3788
3789TEST_F(TypeExtensionApiTest,
3790 FromSpecWithBasesWithoutGcFlagInheritsObjectDelIfNotDefined) {
3791 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3792 "SubclassedType", Py_nb_add, &emptyBinaryFunc, /*base=*/nullptr));
3793 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3794 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3795 auto free_func = reinterpret_cast<freefunc>(PyType_GetSlot(tp, Py_tp_free));
3796 EXPECT_EQ(free_func, &PyObject_Del);
3797}
3798
3799TEST_F(TypeExtensionApiTest,
3800 FromSpecWithBasesWithGcFlagInheritsObjectGcDelIfNotDefined) {
3801 static PyType_Slot slots[1];
3802 slots[0] = {0, nullptr};
3803 static PyType_Spec spec;
3804 spec = {
3805 "__main__.SubclassedType",
3806 0,
3807 0,
3808 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
3809 slots,
3810 };
3811 PyObjectPtr type(PyType_FromSpecWithBases(&spec, /*bases=*/nullptr));
3812 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
3813 auto free_func = reinterpret_cast<freefunc>(PyType_GetSlot(tp, Py_tp_free));
3814 EXPECT_EQ(free_func, &PyObject_GC_Del);
3815}
3816
3817TEST_F(TypeExtensionApiTest, MethodIsInheirtedFromClassFromWinningParent) {
3818 // class C:
3819 // def __int__(self):
3820 // return 11
3821 unaryfunc c_int_func = [](PyObject*) { return PyLong_FromLong(11); };
3822 static PyType_Slot c_slots[2];
3823 c_slots[0] = {Py_nb_int, reinterpret_cast<void*>(c_int_func)};
3824 c_slots[1] = {0, nullptr};
3825 static PyType_Spec c_spec;
3826 c_spec = {
3827 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, c_slots,
3828 };
3829 PyObjectPtr c_type(PyType_FromSpec(&c_spec));
3830 ASSERT_NE(c_type, nullptr);
3831 ASSERT_EQ(PyType_CheckExact(c_type), 1);
3832
3833 // class D(C):
3834 // def __int__(self):
3835 // return 22
3836 unaryfunc d_int_func = [](PyObject*) { return PyLong_FromLong(22); };
3837 static PyType_Slot d_slots[2];
3838 d_slots[0] = {Py_nb_int, reinterpret_cast<void*>(d_int_func)};
3839 d_slots[1] = {0, nullptr};
3840 static PyType_Spec d_spec;
3841 d_spec = {
3842 "__main__.D", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, d_slots,
3843 };
3844 PyObjectPtr d_bases(PyTuple_Pack(1, c_type.get()));
3845 PyObjectPtr d_type(PyType_FromSpecWithBases(&d_spec, d_bases));
3846 ASSERT_NE(d_type, nullptr);
3847 ASSERT_EQ(PyType_CheckExact(d_type), 1);
3848
3849 // class B(C): pass
3850 static PyType_Slot b_slots[1];
3851 b_slots[0] = {0, nullptr};
3852 static PyType_Spec b_spec;
3853 b_spec = {
3854 "__main__.B", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, d_slots,
3855 };
3856 PyObjectPtr b_bases(PyTuple_Pack(1, c_type.get()));
3857 PyObjectPtr b_type(PyType_FromSpecWithBases(&b_spec, b_bases));
3858 ASSERT_NE(b_type, nullptr);
3859 ASSERT_EQ(PyType_CheckExact(b_type), 1);
3860
3861 // class A(B, C): pass
3862 static PyType_Slot a_slots[1];
3863 a_slots[0] = {0, nullptr};
3864 static PyType_Spec a_spec;
3865 a_spec = {
3866 "__main__.A", 0, 0, Py_TPFLAGS_DEFAULT, a_slots,
3867 };
3868 PyObjectPtr a_bases(PyTuple_Pack(2, b_type.get(), d_type.get()));
3869 PyObjectPtr a_type(PyType_FromSpecWithBases(&a_spec, a_bases));
3870 ASSERT_NE(a_type, nullptr);
3871 ASSERT_EQ(PyType_CheckExact(a_type), 1);
3872
3873 // MRO is (A, B, D, C, object)
3874 ASSERT_EQ(moduleSet("__main__", "A", a_type), 0);
3875 PyRun_SimpleString(R"(
3876a_mro = A.__mro__
3877)");
3878 PyObjectPtr a_mro(mainModuleGet("a_mro"));
3879 ASSERT_EQ(PyTuple_Check(a_mro), 1);
3880 ASSERT_EQ(PyTuple_GetItem(a_mro, 0), a_type);
3881 ASSERT_EQ(PyTuple_GetItem(a_mro, 1), b_type);
3882 ASSERT_EQ(PyTuple_GetItem(a_mro, 2), d_type);
3883 ASSERT_EQ(PyTuple_GetItem(a_mro, 3), c_type);
3884
3885 // Even though B inherited Py_tp_int from C, A should inherit it until
3886 // the first concrete definition, which is in D.
3887 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(a_type.get());
3888 void* int_slot = PyType_GetSlot(tp, Py_nb_int);
3889 ASSERT_NE(int_slot, nullptr);
3890 EXPECT_NE(reinterpret_cast<unaryfunc>(int_slot), c_int_func);
3891 EXPECT_EQ(reinterpret_cast<unaryfunc>(int_slot), d_int_func);
3892}
3893
3894TEST_F(TypeExtensionApiTest,
3895 FromSpecWithBasesInheritsGcFlagAndTraverseClearSlots) {
3896 traverseproc empty_traverse_func = [](PyObject*, visitproc, void*) {
3897 return 0;
3898 };
3899 inquiry empty_clear_func = [](PyObject*) { return 0; };
3900 static PyType_Slot base_slots[3];
3901 base_slots[0] = {Py_tp_traverse,
3902 reinterpret_cast<void*>(empty_traverse_func)};
3903 base_slots[1] = {Py_tp_clear, reinterpret_cast<void*>(empty_clear_func)};
3904 base_slots[2] = {0, nullptr};
3905 static PyType_Spec base_spec;
3906 base_spec = {
3907 "__main__.BaseType",
3908 0,
3909 0,
3910 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
3911 base_slots,
3912 };
3913 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
3914 ASSERT_NE(base_type, nullptr);
3915 ASSERT_EQ(PyType_CheckExact(base_type), 1);
3916
3917 static PyType_Slot slots[1];
3918 slots[0] = {0, nullptr};
3919 static PyType_Spec spec;
3920 spec = {
3921 "__main__.SubclassedType", 0, 0, Py_TPFLAGS_DEFAULT, slots,
3922 };
3923 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
3924 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
3925 ASSERT_NE(type, nullptr);
3926 ASSERT_EQ(PyType_CheckExact(type), 1);
3927
3928 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(base_type.get());
3929 EXPECT_NE(PyType_GetFlags(tp) & Py_TPFLAGS_HAVE_GC, 0UL);
3930 EXPECT_EQ(reinterpret_cast<traverseproc>(PyType_GetSlot(tp, Py_tp_traverse)),
3931 empty_traverse_func);
3932 EXPECT_EQ(reinterpret_cast<inquiry>(PyType_GetSlot(tp, Py_tp_clear)),
3933 empty_clear_func);
3934}
3935
3936TEST_F(TypeExtensionApiTest, FromSpecWithBasesInheritsNew) {
3937 newfunc empty_new_func = [](PyTypeObject*, PyObject*, PyObject*) {
3938 return Py_None;
3939 };
3940 ASSERT_NO_FATAL_FAILURE(
3941 createTypeWithSlot("BaseType", Py_tp_new, empty_new_func));
3942 PyObjectPtr base_type(mainModuleGet("BaseType"));
3943 ASSERT_NO_FATAL_FAILURE(createTypeWithSlotAndBase(
3944 "SubclassedType", Py_nb_add, &emptyBinaryFunc, base_type));
3945 PyObjectPtr subclassed_type(mainModuleGet("SubclassedType"));
3946
3947 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(subclassed_type.get());
3948 EXPECT_EQ(reinterpret_cast<newfunc>(PyType_GetSlot(tp, Py_tp_new)),
3949 empty_new_func);
3950}
3951
3952TEST_F(TypeExtensionApiTest,
3953 FromSpecWithMixedBasesSetsExtensionAsDominantBase) {
3954 struct ExtensionObject {
3955 PyObject_HEAD
3956 int native_data;
3957 };
3958
3959 static PyType_Slot extension_slots[2];
3960 extension_slots[0] = {0, nullptr};
3961
3962 unsigned int flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
3963 static PyType_Spec extension_spec = {
3964 "__main__.ExtensionBaseClass",
3965 sizeof(ExtensionObject),
3966 0,
3967 flags,
3968 extension_slots,
3969 };
3970 PyObjectPtr extension_basetype(PyType_FromSpec(&extension_spec));
3971 ASSERT_EQ(PyType_CheckExact(extension_basetype), 1);
3972 ASSERT_EQ(moduleSet("__main__", "ExtensionBaseClass", extension_basetype), 0);
3973
3974 PyRun_SimpleString(R"(
3975class SimpleManagedBaseClass: pass
3976
3977class Base(ExtensionBaseClass): pass
3978
3979class SubClass(SimpleManagedBaseClass, Base): pass
3980)");
3981
3982 PyObjectPtr base(mainModuleGet("Base"));
3983 ASSERT_NE(base, nullptr);
3984 PyObjectPtr subclass(mainModuleGet("SubClass"));
3985 ASSERT_NE(subclass, nullptr);
3986 PyObjectPtr subclass_base(PyObject_GetAttrString(subclass, "__base__"));
3987 EXPECT_EQ(subclass_base.get(), base.get());
3988}
3989
3990TEST_F(TypeExtensionApiTest, FromSpecWithoutBasicSizeInheritsDefaultBasicSize) {
3991 static PyType_Slot slots[1];
3992 slots[0] = {0, nullptr};
3993 static PyType_Spec spec;
3994 spec = {
3995 "__main__.Foo", 0, 0, Py_TPFLAGS_DEFAULT, slots,
3996 };
3997 PyObjectPtr type(PyType_FromSpec(&spec));
3998 ASSERT_NE(type, nullptr);
3999 ASSERT_EQ(PyType_CheckExact(type), 1);
4000
4001 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4002 EXPECT_EQ(_PyObject_SIZE(tp), static_cast<Py_ssize_t>(sizeof(PyObject)));
4003}
4004
4005TEST_F(TypeExtensionApiTest, FromSpecWithoutAllocInheritsDefaultAlloc) {
4006 static PyType_Slot slots[1];
4007 slots[0] = {0, nullptr};
4008 static PyType_Spec spec;
4009 spec = {
4010 "__main__.Foo", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT, slots,
4011 };
4012 PyObjectPtr type(PyType_FromSpec(&spec));
4013 ASSERT_NE(type, nullptr);
4014 ASSERT_EQ(PyType_CheckExact(type), 1);
4015
4016 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4017 ASSERT_EQ(reinterpret_cast<allocfunc>(PyType_GetSlot(tp, Py_tp_alloc)),
4018 &PyType_GenericAlloc);
4019}
4020
4021TEST_F(TypeExtensionApiTest, FromSpecWithoutNewInheritsDefaultNew) {
4022 static PyType_Slot slots[1];
4023 slots[0] = {0, nullptr};
4024 static PyType_Spec spec;
4025 spec = {
4026 "__main__.Foo", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT, slots,
4027 };
4028 PyObjectPtr type(PyType_FromSpec(&spec));
4029 ASSERT_NE(type, nullptr);
4030 ASSERT_EQ(PyType_CheckExact(type), 1);
4031 ASSERT_EQ(moduleSet("__main__", "Foo", type), 0);
4032
4033 // In Pyro tp_new = PyType_GenericNew, in CPython tp_new = object_new
4034 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4035 ASSERT_NE(PyType_GetSlot(tp, Py_tp_new), nullptr);
4036}
4037
4038TEST_F(TypeExtensionApiTest, FromSpecWithoutDeallocInheritsDefaultDealloc) {
4039 struct FooObject {
4040 PyObject_HEAD
4041 int native_data;
4042 };
4043 static PyType_Slot slots[1];
4044 slots[0] = {0, nullptr};
4045 static PyType_Spec spec;
4046 spec = {
4047 "__main__.Foo", sizeof(FooObject), 0, Py_TPFLAGS_DEFAULT, slots,
4048 };
4049 PyObjectPtr type(PyType_FromSpec(&spec));
4050 ASSERT_NE(type, nullptr);
4051 ASSERT_EQ(PyType_CheckExact(type), 1);
4052
4053 // type inherited subclassDealloc
4054 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4055 ASSERT_NE(PyType_GetSlot(tp, Py_tp_dealloc), nullptr);
4056 Py_ssize_t type_refcnt = Py_REFCNT(tp);
4057
4058 // Create an instance
4059 FooObject* instance = PyObject_New(FooObject, tp);
4060 ASSERT_GE(Py_REFCNT(instance), 1); // CPython
4061 ASSERT_LE(Py_REFCNT(instance), 2); // Pyro
4062 ASSERT_EQ(Py_REFCNT(tp), type_refcnt + 1);
4063
4064 // Trigger a tp_dealloc
4065 Py_DECREF(instance);
4066 collectGarbage();
4067 ASSERT_EQ(Py_REFCNT(tp), type_refcnt);
4068}
4069
4070TEST_F(TypeExtensionApiTest, DefaultDeallocCallsDelAndFinalize) {
4071 struct FooObject {
4072 PyObject_HEAD
4073 };
4074 destructor del_func = [](PyObject*) {
4075 moduleSet("__main__", "called_del", Py_True);
4076 };
4077 static PyType_Slot slots[2];
4078 slots[0] = {Py_tp_del, reinterpret_cast<void*>(del_func)};
4079 slots[1] = {0, nullptr};
4080 static PyType_Spec spec;
4081 spec = {
4082 "__main__.Foo", sizeof(FooObject), 0, Py_TPFLAGS_DEFAULT, slots,
4083 };
4084 PyObjectPtr type(PyType_FromSpec(&spec));
4085 ASSERT_NE(type, nullptr);
4086 ASSERT_EQ(PyType_CheckExact(type), 1);
4087
4088 // type inherited subclassDealloc
4089 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4090 ASSERT_NE(PyType_GetSlot(tp, Py_tp_dealloc), nullptr);
4091 Py_ssize_t type_refcnt = Py_REFCNT(tp);
4092
4093 // Create an instance
4094 FooObject* instance = PyObject_New(FooObject, tp);
4095 ASSERT_GE(Py_REFCNT(instance), 1); // CPython
4096 ASSERT_LE(Py_REFCNT(instance), 2); // Pyro
4097 ASSERT_EQ(Py_REFCNT(tp), type_refcnt + 1);
4098
4099 // Trigger a tp_dealloc
4100 Py_DECREF(instance);
4101 collectGarbage();
4102 ASSERT_EQ(Py_REFCNT(tp), type_refcnt);
4103 PyObjectPtr called_del(testing::mainModuleGet("called_del"));
4104 EXPECT_EQ(called_del, Py_True);
4105}
4106
4107TEST_F(TypeExtensionApiTest, FromSpecWithBasesSubclassInheritsParentDealloc) {
4108 struct FooObject {
4109 PyObject_HEAD
4110 };
4111 struct FooSubclassObject {
4112 FooObject base;
4113 };
4114 destructor dealloc_func = [](PyObject* self) {
4115 PyTypeObject* tp = Py_TYPE(self);
4116 PyObject_Del(self);
4117 Py_DECREF(tp);
4118 };
4119 static PyType_Slot base_slots[2];
4120 base_slots[0] = {Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)};
4121 base_slots[1] = {0, nullptr};
4122 static PyType_Spec base_spec;
4123 base_spec = {
4124 "__main__.BaseType",
4125 sizeof(FooObject),
4126 0,
4127 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
4128 base_slots,
4129 };
4130 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
4131 ASSERT_NE(base_type, nullptr);
4132 ASSERT_EQ(PyType_CheckExact(base_type), 1);
4133
4134 static PyType_Slot slots[1];
4135 slots[0] = {0, nullptr};
4136 static PyType_Spec spec;
4137 spec = {
4138 "__main__.SubclassedType",
4139 sizeof(FooSubclassObject),
4140 0,
4141 Py_TPFLAGS_DEFAULT,
4142 slots,
4143 };
4144 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
4145 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
4146 ASSERT_NE(type, nullptr);
4147 ASSERT_EQ(PyType_CheckExact(type), 1);
4148
4149 // type inherited subclassDealloc
4150 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4151 ASSERT_NE(PyType_GetSlot(tp, Py_tp_dealloc), nullptr);
4152 Py_ssize_t type_refcnt = Py_REFCNT(tp);
4153
4154 // Create an instance
4155 FooObject* instance = PyObject_New(FooObject, tp);
4156 ASSERT_GE(Py_REFCNT(instance), 1); // CPython
4157 ASSERT_LE(Py_REFCNT(instance), 2); // Pyro
4158 ASSERT_EQ(Py_REFCNT(tp), type_refcnt + 1);
4159
4160 // Trigger a tp_dealloc
4161 Py_DECREF(instance);
4162 collectGarbage();
4163 ASSERT_EQ(Py_REFCNT(tp), type_refcnt);
4164}
4165
4166TEST_F(TypeExtensionApiTest, FromSpecWithBasesSubclassInheritsDefaultDealloc) {
4167 struct FooObject {
4168 PyObject_HEAD
4169 int native_data;
4170 };
4171 struct FooSubclassObject {
4172 FooObject base;
4173 };
4174 static PyType_Slot base_slots[1];
4175 base_slots[0] = {0, nullptr};
4176 static PyType_Spec base_spec;
4177 base_spec = {
4178 "__main__.BaseType",
4179 sizeof(FooObject),
4180 0,
4181 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
4182 base_slots,
4183 };
4184 PyObjectPtr base_type(PyType_FromSpec(&base_spec));
4185 ASSERT_NE(base_type, nullptr);
4186 ASSERT_EQ(PyType_CheckExact(base_type), 1);
4187
4188 static PyType_Slot slots[1];
4189 slots[0] = {0, nullptr};
4190 static PyType_Spec spec;
4191 spec = {
4192 "__main__.SubclassedType",
4193 sizeof(FooSubclassObject),
4194 0,
4195 Py_TPFLAGS_DEFAULT,
4196 slots,
4197 };
4198 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
4199 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
4200 ASSERT_NE(type, nullptr);
4201 ASSERT_EQ(PyType_CheckExact(type), 1);
4202
4203 // type inherited subclassDealloc
4204 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4205 ASSERT_NE(PyType_GetSlot(tp, Py_tp_dealloc), nullptr);
4206 Py_ssize_t type_refcnt = Py_REFCNT(tp);
4207
4208 // Create an instance
4209 FooObject* instance = PyObject_New(FooObject, tp);
4210 ASSERT_GE(Py_REFCNT(instance), 1); // CPython
4211 ASSERT_LE(Py_REFCNT(instance), 2); // Pyro
4212 ASSERT_EQ(Py_REFCNT(tp), type_refcnt + 1);
4213
4214 // Trigger a tp_dealloc
4215 Py_DECREF(instance);
4216 collectGarbage();
4217 ASSERT_EQ(Py_REFCNT(tp), type_refcnt);
4218}
4219
4220TEST_F(TypeExtensionApiTest, TypeLookupSkipsInstanceDictionary) {
4221 PyRun_SimpleString(R"(
4222class Foo:
4223 bar = 2
4224
4225foo = Foo()
4226foo.bar = 1
4227)");
4228 PyObjectPtr foo(mainModuleGet("foo"));
4229 PyObjectPtr foo_type(PyObject_Type(foo));
4230 PyObjectPtr bar_str(PyUnicode_FromString("bar"));
4231 PyObject* res =
4232 _PyType_Lookup(reinterpret_cast<PyTypeObject*>(foo_type.get()), bar_str);
4233 ASSERT_EQ(PyErr_Occurred(), nullptr);
4234 ASSERT_NE(res, nullptr);
4235 EXPECT_TRUE(isLongEqualsLong(res, 2));
4236}
4237
4238TEST_F(TypeExtensionApiTest, TypeLookupWithoutMatchDoesNotRaise) {
4239 PyRun_SimpleString(R"(
4240class Foo: pass
4241)");
4242 PyObjectPtr foo_type(mainModuleGet("Foo"));
4243 PyObjectPtr bar_str(PyUnicode_FromString("bar"));
4244 PyObjectPtr res(
4245 _PyType_Lookup(reinterpret_cast<PyTypeObject*>(foo_type.get()), bar_str));
4246 ASSERT_EQ(PyErr_Occurred(), nullptr);
4247 EXPECT_EQ(res, nullptr);
4248}
4249
4250TEST_F(TypeExtensionApiTest, TypeLookupWithNonStrDoesNotRaise) {
4251 PyRun_SimpleString(R"(
4252class Foo: pass
4253)");
4254 PyObjectPtr foo_type(mainModuleGet("Foo"));
4255 PyObjectPtr res(
4256 _PyType_Lookup(reinterpret_cast<PyTypeObject*>(foo_type.get()), Py_None));
4257 ASSERT_EQ(PyErr_Occurred(), nullptr);
4258 EXPECT_EQ(res, nullptr);
4259}
4260
4261TEST_F(TypeExtensionApiTest, FromSpecWithGCFlagCallsDealloc) {
4262 destructor dealloc_func = [](PyObject* self) {
4263 moduleSet("__main__", "called_del", Py_True);
4264 PyTypeObject* type = Py_TYPE(self);
4265 PyObject_GC_UnTrack(self);
4266 PyObject_GC_Del(self);
4267 Py_DECREF(type);
4268 };
4269 static PyType_Slot slots[2];
4270 slots[0] = {Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)};
4271 slots[1] = {0, nullptr};
4272 static PyType_Spec spec;
4273 spec = {
4274 "__main__.Foo",
4275 sizeof(PyObject),
4276 0,
4277 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
4278 slots,
4279 };
4280 PyObjectPtr type(PyType_FromSpec(&spec));
4281 ASSERT_NE(type, nullptr);
4282 ASSERT_EQ(PyType_CheckExact(type), 1);
4283
4284 PyTypeObject* tp = reinterpret_cast<PyTypeObject*>(type.get());
4285 ASSERT_NE(PyType_GetSlot(tp, Py_tp_dealloc), nullptr);
4286 Py_ssize_t type_refcnt = Py_REFCNT(tp);
4287
4288 // Create an instance
4289 PyObject* instance = PyObject_GC_New(PyObject, tp);
4290 PyObject_GC_Track(instance);
4291 ASSERT_GE(Py_REFCNT(instance), 1); // CPython
4292 ASSERT_LE(Py_REFCNT(instance), 2); // Pyro
4293 ASSERT_EQ(Py_REFCNT(tp), type_refcnt + 1);
4294
4295 // Trigger a tp_dealloc
4296 Py_DECREF(instance);
4297 collectGarbage();
4298 ASSERT_EQ(Py_REFCNT(tp), type_refcnt);
4299 PyObjectPtr called_del(testing::mainModuleGet("called_del"));
4300 EXPECT_EQ(called_del, Py_True);
4301}
4302
4303TEST_F(TypeExtensionApiTest, ManagedTypeInheritsTpFlagsFromCType) {
4304 ASSERT_NO_FATAL_FAILURE(createBarTypeWithMembers());
4305 ASSERT_EQ(PyRun_SimpleString(R"(
4306class Baz(Bar): pass
4307)"),
4308 0);
4309 PyObjectPtr baz_type(mainModuleGet("Baz"));
4310 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(baz_type.get())) &
4311 Py_TPFLAGS_HEAPTYPE);
4312}
4313
4314TEST_F(TypeExtensionApiTest, ManagedTypeInheritsFromCType) {
4315 ASSERT_NO_FATAL_FAILURE(createBarTypeWithMembers());
4316 ASSERT_EQ(PyRun_SimpleString(R"(
4317r1 = Bar().t_bool
4318class Baz(Bar): pass
4319r2 = Baz().t_bool
4320r3 = Baz().t_object
4321)"),
4322 0);
4323 PyObjectPtr r1(mainModuleGet("r1"));
4324 ASSERT_EQ(PyBool_Check(r1), 1);
4325 EXPECT_EQ(r1, Py_True);
4326 PyObjectPtr r2(mainModuleGet("r2"));
4327 ASSERT_EQ(PyBool_Check(r2), 1);
4328 EXPECT_EQ(r2, Py_True);
4329 PyObjectPtr r3(mainModuleGet("r3"));
4330 ASSERT_EQ(PyList_Check(r3), 1);
4331 EXPECT_EQ(PyList_Size(r3), 0);
4332}
4333
4334TEST_F(TypeExtensionApiTest, ManagedTypeWithLayoutInheritsFromCType) {
4335 ASSERT_NO_FATAL_FAILURE(createBarTypeWithMembers());
4336 ASSERT_EQ(PyRun_SimpleString(R"(
4337class Baz(Bar):
4338 def __init__(self):
4339 self.value = 123
4340baz = Baz()
4341r1 = baz.t_bool
4342r2 = baz.value
4343r3 = baz.t_object
4344)"),
4345 0);
4346 PyObjectPtr baz(mainModuleGet("baz"));
4347 ASSERT_NE(baz, nullptr);
4348 PyObjectPtr r1(mainModuleGet("r1"));
4349 ASSERT_EQ(PyBool_Check(r1), 1);
4350 EXPECT_EQ(r1, Py_False);
4351 PyObjectPtr r2(mainModuleGet("r2"));
4352 EXPECT_TRUE(isLongEqualsLong(r2, 123));
4353 PyObjectPtr r3(mainModuleGet("r3"));
4354 EXPECT_FALSE(PyList_Check(r3));
4355}
4356
4357TEST_F(TypeExtensionApiTest, CTypeWithSlotsBuiltinBaseTpNewCreatesNewInstance) {
4358 static PyType_Slot base_slots[1];
4359 base_slots[0] = {0, nullptr};
4360 static PyType_Spec base_spec;
4361 base_spec = {
4362 "__main__.BaseType", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
4363 base_slots,
4364 };
4365 PyObjectPtr base_bases(PyTuple_Pack(1, PyExc_Exception));
4366 PyObjectPtr base_type(PyType_FromSpecWithBases(&base_spec, base_bases));
4367 ASSERT_NE(base_type, nullptr);
4368 ASSERT_EQ(PyType_CheckExact(base_type), 1);
4369 ASSERT_EQ(moduleSet("__main__", "BaseType", base_type), 0);
4370
4371 newfunc new_func = [](PyTypeObject* t, PyObject* a, PyObject* k) {
4372 PyObjectPtr base(mainModuleGet("BaseType"));
4373
4374 newfunc base_tp_new = reinterpret_cast<newfunc>(
4375 PyType_GetSlot(base.asTypeObject(), Py_tp_new));
4376 return base_tp_new(t, a, k);
4377 };
4378
4379 static PyType_Slot slots[2];
4380 slots[0] = {Py_tp_new, reinterpret_cast<void*>(new_func)};
4381 slots[1] = {0, nullptr};
4382 static PyType_Spec spec;
4383 spec = {
4384 "__main__.SubclassedType", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4385 };
4386 PyObjectPtr bases(PyTuple_Pack(1, base_type.get()));
4387 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
4388 ASSERT_NE(type, nullptr);
4389 ASSERT_EQ(PyType_CheckExact(type), 1);
4390 PyObjectPtr instance(_PyObject_CallNoArg(type));
4391 ASSERT_NE(instance, nullptr);
4392 ASSERT_EQ(Py_TYPE(instance.get()), type.asTypeObject());
4393}
4394
4395TEST_F(TypeExtensionApiTest,
4396 CTypeWithSlotsBuiltinBaseTpDeallocFreesInstancePyro) {
4397 destructor dealloc_func = [](PyObject* o) {
4398 PyTypeObject* tp = Py_TYPE(o);
4399 // Call the base type Py_tp_dealloc slot
4400 destructor base_dealloc = reinterpret_cast<destructor>(PyType_GetSlot(
4401 reinterpret_cast<PyTypeObject*>(PyExc_Exception), Py_tp_dealloc));
4402 (*base_dealloc)(o);
4403 Py_DECREF(tp);
4404 };
4405
4406 static PyType_Slot slots[2];
4407 slots[0] = {Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)};
4408 slots[1] = {0, nullptr};
4409 static PyType_Spec spec;
4410 spec = {
4411 "__main__.SubclassedType", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4412 };
4413 PyObjectPtr bases(PyTuple_Pack(1, PyExc_Exception));
4414 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
4415 ASSERT_NE(type, nullptr);
4416 ASSERT_EQ(PyType_CheckExact(type), 1);
4417 Py_ssize_t type_refcnt = Py_REFCNT(type);
4418 PyObject* instance = _PyObject_CallNoArg(type);
4419 ASSERT_NE(instance, nullptr);
4420 ASSERT_EQ(Py_TYPE(instance), type.asTypeObject());
4421 ASSERT_EQ(Py_REFCNT(type), type_refcnt + 1);
4422 Py_DECREF(instance);
4423 collectGarbage();
4424 ASSERT_EQ(Py_REFCNT(type), type_refcnt);
4425}
4426
4427TEST_F(TypeExtensionApiTest, CTypeInheritsFromManagedType) {
4428 ASSERT_EQ(PyRun_SimpleString(R"(
4429class Foo:
4430 def foo(self):
4431 return 123
4432)"),
4433 0);
4434 PyObjectPtr foo_type(mainModuleGet("Foo"));
4435
4436 struct FooObject {
4437 PyObject_HEAD
4438 PyObject* dict;
4439 int t_int;
4440 };
4441 static PyMemberDef members[2];
4442 members[0] = {"t_int", T_INT, offsetof(FooObject, t_int)};
4443 members[1] = {nullptr};
4444 initproc init_func = [](PyObject* self, PyObject*, PyObject*) {
4445 reinterpret_cast<FooObject*>(self)->t_int = 321;
4446 return 0;
4447 };
4448 destructor dealloc_func = [](PyObject* self) {
4449 PyTypeObject* type = Py_TYPE(self);
4450 PyObject_GC_UnTrack(self);
4451 PyObject_GC_Del(self);
4452 Py_DECREF(type);
4453 };
4454 static PyType_Slot slots[4];
4455 slots[0] = {Py_tp_init, reinterpret_cast<void*>(init_func)};
4456 slots[1] = {Py_tp_members, reinterpret_cast<void*>(members)};
4457 slots[2] = {Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
4458 slots[3] = {0, nullptr};
4459 static PyType_Spec spec;
4460 spec = {
4461 "__main__.FooSubclass",
4462 sizeof(FooObject),
4463 0,
4464 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
4465 slots,
4466 };
4467
4468 PyObjectPtr bases(PyTuple_Pack(1, foo_type.get()));
4469 PyObjectPtr type(PyType_FromSpecWithBases(&spec, bases));
4470 ASSERT_NE(type, nullptr);
4471 ASSERT_EQ(PyType_CheckExact(type), 1);
4472 ASSERT_EQ(moduleSet("__main__", "FooSubclass", type), 0);
4473
4474 ASSERT_EQ(PyRun_SimpleString(R"(
4475r1 = FooSubclass().foo()
4476r2 = FooSubclass().t_int
4477)"),
4478 0);
4479 PyObjectPtr r1(mainModuleGet("r1"));
4480 EXPECT_TRUE(isLongEqualsLong(r1, 123));
4481 PyObjectPtr r2(mainModuleGet("r2"));
4482 EXPECT_TRUE(isLongEqualsLong(r2, 321));
4483}
4484
4485TEST_F(TypeExtensionApiTest, MethodsMethFastWithKeywordsCallNoArg) {
4486 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const*,
4487 Py_ssize_t nargs, PyObject* kwnames) {
4488 EXPECT_EQ(kwnames, nullptr);
4489 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4490 return PyTuple_Pack(2, self, nargs_obj.get());
4491 };
4492 static PyMethodDef methods[] = {
4493 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4494 METH_FASTCALL | METH_KEYWORDS},
4495 {nullptr}};
4496 PyType_Slot slots[] = {
4497 {Py_tp_methods, methods},
4498 {0, nullptr},
4499 };
4500 static PyType_Spec spec;
4501 spec = {
4502 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4503 };
4504
4505 PyObjectPtr type(PyType_FromSpec(&spec));
4506 ASSERT_NE(type, nullptr);
4507 testing::moduleSet("__main__", "C", type);
4508
4509 PyRun_SimpleString(R"(
4510self = C()
4511result = self.fastcall()
4512)");
4513 PyObjectPtr self(testing::mainModuleGet("self"));
4514 PyObjectPtr result(testing::mainModuleGet("result"));
4515 ASSERT_NE(result, nullptr);
4516 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4517 ASSERT_EQ(PyTuple_Size(result), 2);
4518 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4519 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 0));
4520}
4521
4522TEST_F(TypeExtensionApiTest, MethodsMethFastWithKeywordsCallPosCall) {
4523 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4524 Py_ssize_t nargs, PyObject* kwnames) {
4525 EXPECT_EQ(kwnames, nullptr);
4526 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4527 return PyTuple_Pack(3, self, args[0], nargs_obj.get());
4528 };
4529 static PyMethodDef methods[] = {
4530 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4531 METH_FASTCALL | METH_KEYWORDS},
4532 {nullptr}};
4533 PyType_Slot slots[] = {
4534 {Py_tp_methods, methods},
4535 {0, nullptr},
4536 };
4537 static PyType_Spec spec;
4538 spec = {
4539 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4540 };
4541
4542 PyObjectPtr type(PyType_FromSpec(&spec));
4543 ASSERT_NE(type, nullptr);
4544 testing::moduleSet("__main__", "C", type);
4545
4546 PyRun_SimpleString(R"(
4547self = C()
4548result = self.fastcall(1234)
4549)");
4550 PyObjectPtr self(testing::mainModuleGet("self"));
4551 PyObjectPtr result(testing::mainModuleGet("result"));
4552 ASSERT_NE(result, nullptr);
4553 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4554 ASSERT_EQ(PyTuple_Size(result), 3);
4555 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4556 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4557 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 1));
4558}
4559
4560TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsPosCallMultiArgs) {
4561 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4562 Py_ssize_t nargs, PyObject* kwnames) {
4563 EXPECT_EQ(kwnames, nullptr);
4564 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4565 return PyTuple_Pack(4, self, args[0], args[1], nargs_obj.get());
4566 };
4567 static PyMethodDef methods[] = {
4568 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4569 METH_FASTCALL | METH_KEYWORDS},
4570 {nullptr}};
4571 PyType_Slot slots[] = {
4572 {Py_tp_methods, methods},
4573 {0, nullptr},
4574 };
4575 static PyType_Spec spec;
4576 spec = {
4577 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4578 };
4579
4580 PyObjectPtr type(PyType_FromSpec(&spec));
4581 ASSERT_NE(type, nullptr);
4582 testing::moduleSet("__main__", "C", type);
4583
4584 PyRun_SimpleString(R"(
4585self = C()
4586result = self.fastcall(1234, 5678)
4587)");
4588 PyObjectPtr self(testing::mainModuleGet("self"));
4589 PyObjectPtr result(testing::mainModuleGet("result"));
4590 ASSERT_NE(result, nullptr);
4591 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4592 ASSERT_EQ(PyTuple_Size(result), 4);
4593 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4594 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4595 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 5678));
4596 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 3), 2));
4597}
4598
4599TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsKwCall) {
4600 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4601 Py_ssize_t nargs, PyObject* kwnames) {
4602 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4603 return PyTuple_Pack(5, self, args[0], args[1], nargs_obj.get(), kwnames);
4604 };
4605 static PyMethodDef methods[] = {
4606 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4607 METH_FASTCALL | METH_KEYWORDS},
4608 {nullptr}};
4609 PyType_Slot slots[] = {
4610 {Py_tp_methods, methods},
4611 {0, nullptr},
4612 };
4613 static PyType_Spec spec;
4614 spec = {
4615 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4616 };
4617
4618 PyObjectPtr type(PyType_FromSpec(&spec));
4619 ASSERT_NE(type, nullptr);
4620 testing::moduleSet("__main__", "C", type);
4621
4622 PyRun_SimpleString(R"(
4623self = C()
4624result = self.fastcall(1234, kwarg=5678)
4625)");
4626 PyObjectPtr self(testing::mainModuleGet("self"));
4627 PyObjectPtr result(testing::mainModuleGet("result"));
4628 ASSERT_NE(result, nullptr);
4629 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4630 ASSERT_EQ(PyTuple_Size(result), 5);
4631 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4632 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4633 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 5678));
4634 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 3), 1));
4635
4636 PyObject* kwnames = PyTuple_GetItem(result, 4);
4637 ASSERT_EQ(PyTuple_CheckExact(kwnames), 1);
4638 ASSERT_EQ(PyTuple_Size(kwnames), 1);
4639 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 0), "kwarg"));
4640}
4641
4642TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsKwCallMultiArg) {
4643 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4644 Py_ssize_t nargs, PyObject* kwnames) {
4645 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4646 return PyTuple_Pack(7, self, args[0], args[1], args[2], args[3],
4647 nargs_obj.get(), kwnames);
4648 };
4649 static PyMethodDef methods[] = {
4650 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4651 METH_FASTCALL | METH_KEYWORDS},
4652 {nullptr}};
4653 PyType_Slot slots[] = {
4654 {Py_tp_methods, methods},
4655 {0, nullptr},
4656 };
4657 static PyType_Spec spec;
4658 spec = {
4659 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4660 };
4661
4662 PyObjectPtr type(PyType_FromSpec(&spec));
4663 ASSERT_NE(type, nullptr);
4664 testing::moduleSet("__main__", "C", type);
4665
4666 PyRun_SimpleString(R"(
4667self = C()
4668result = self.fastcall(1234, 99, kwarg=5678, kwdos=22)
4669)");
4670 PyObjectPtr self(testing::mainModuleGet("self"));
4671 PyObjectPtr result(testing::mainModuleGet("result"));
4672 ASSERT_NE(result, nullptr);
4673 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4674 ASSERT_EQ(PyTuple_Size(result), 7);
4675 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4676 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4677 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 99));
4678 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 3), 5678));
4679 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 4), 22));
4680 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 5), 2));
4681
4682 PyObject* kwnames = PyTuple_GetItem(result, 6);
4683 ASSERT_EQ(PyTuple_CheckExact(kwnames), 1);
4684 ASSERT_EQ(PyTuple_Size(kwnames), 2);
4685 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 0), "kwarg"));
4686 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 1), "kwdos"));
4687}
4688
4689TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsExCall) {
4690 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4691 Py_ssize_t nargs, PyObject* kwnames) {
4692 PyObjectPtr nargs_obj(PyLong_FromLong(nargs));
4693 return PyTuple_Pack(5, self, args[0], args[1], nargs_obj.get(), kwnames);
4694 };
4695 static PyMethodDef methods[] = {
4696 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4697 METH_FASTCALL | METH_KEYWORDS},
4698 {nullptr}};
4699 PyType_Slot slots[] = {
4700 {Py_tp_methods, methods},
4701 {0, nullptr},
4702 };
4703 static PyType_Spec spec;
4704 spec = {
4705 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4706 };
4707
4708 PyObjectPtr type(PyType_FromSpec(&spec));
4709 ASSERT_NE(type, nullptr);
4710 testing::moduleSet("__main__", "C", type);
4711
4712 PyRun_SimpleString(R"(
4713self = C()
4714result = self.fastcall(*[1234], kwarg=5678)
4715)");
4716 PyObjectPtr self(testing::mainModuleGet("self"));
4717 PyObjectPtr result(testing::mainModuleGet("result"));
4718 ASSERT_NE(result, nullptr);
4719 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4720 ASSERT_EQ(PyTuple_Size(result), 5);
4721 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4722 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4723 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 5678));
4724 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 3), 1));
4725
4726 PyObject* kwnames = PyTuple_GetItem(result, 4);
4727 ASSERT_EQ(PyTuple_CheckExact(kwnames), 1);
4728 ASSERT_EQ(PyTuple_Size(kwnames), 1);
4729 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 0), "kwarg"));
4730}
4731
4732TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsExCallMultiArg) {
4733 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4734 Py_ssize_t nargs, PyObject* kwnames) {
4735 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4736 return PyTuple_Pack(7, self, args[0], args[1], args[2], args[3],
4737 nargs_obj.get(), kwnames);
4738 };
4739 static PyMethodDef methods[] = {
4740 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4741 METH_FASTCALL | METH_KEYWORDS},
4742 {nullptr}};
4743 PyType_Slot slots[] = {
4744 {Py_tp_methods, methods},
4745 {0, nullptr},
4746 };
4747 static PyType_Spec spec;
4748 spec = {
4749 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4750 };
4751
4752 PyObjectPtr type(PyType_FromSpec(&spec));
4753 ASSERT_NE(type, nullptr);
4754 testing::moduleSet("__main__", "C", type);
4755
4756 PyRun_SimpleString(R"(
4757self = C()
4758result = self.fastcall(*[1234, 99], kwarg=5678, kwdos=22)
4759)");
4760 PyObjectPtr self(testing::mainModuleGet("self"));
4761 PyObjectPtr result(testing::mainModuleGet("result"));
4762 ASSERT_NE(result, nullptr);
4763 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4764 ASSERT_EQ(PyTuple_Size(result), 7);
4765 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4766 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4767 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 99));
4768 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 3), 5678));
4769 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 4), 22));
4770 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 5), 2));
4771
4772 PyObject* kwnames = PyTuple_GetItem(result, 6);
4773 ASSERT_EQ(PyTuple_CheckExact(kwnames), 1);
4774 ASSERT_EQ(PyTuple_Size(kwnames), 2);
4775
4776 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 0), "kwarg"));
4777 EXPECT_TRUE(isUnicodeEqualsCStr(PyTuple_GetItem(kwnames, 1), "kwdos"));
4778}
4779
4780TEST_F(TypeExtensionApiTest, MethodsMethFastCallWithKeywordsExEmptyKwargsCall) {
4781 _PyCFunctionFastWithKeywords meth = [](PyObject* self, PyObject* const* args,
4782 Py_ssize_t nargs, PyObject* kwnames) {
4783 EXPECT_EQ(kwnames, nullptr);
4784 PyObjectPtr nargs_obj(PyLong_FromSsize_t(nargs));
4785 return PyTuple_Pack(3, self, args[0], nargs_obj.get());
4786 };
4787 static PyMethodDef methods[] = {
4788 {"fastcall", reinterpret_cast<PyCFunction>(reinterpret_cast<void*>(meth)),
4789 METH_FASTCALL | METH_KEYWORDS},
4790 {nullptr}};
4791 PyType_Slot slots[] = {
4792 {Py_tp_methods, methods},
4793 {0, nullptr},
4794 };
4795 static PyType_Spec spec;
4796 spec = {
4797 "__main__.C", 0, 0, Py_TPFLAGS_DEFAULT, slots,
4798 };
4799
4800 PyObjectPtr type(PyType_FromSpec(&spec));
4801 ASSERT_NE(type, nullptr);
4802 testing::moduleSet("__main__", "C", type);
4803
4804 PyRun_SimpleString(R"(
4805self = C()
4806result = self.fastcall(*[1234], *{})
4807)");
4808 PyObjectPtr self(testing::mainModuleGet("self"));
4809 PyObjectPtr result(testing::mainModuleGet("result"));
4810 ASSERT_NE(result, nullptr);
4811 ASSERT_EQ(PyTuple_CheckExact(result), 1);
4812 ASSERT_EQ(PyTuple_Size(result), 3);
4813 EXPECT_EQ(PyTuple_GetItem(result, 0), self);
4814 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 1), 1234));
4815 EXPECT_TRUE(isLongEqualsLong(PyTuple_GetItem(result, 2), 1));
4816}
4817
4818TEST(TypeExtensionApiTestNoFixture, DeallocSlotCalledDuringFinalize) {
4819 resetPythonEnv();
4820 Py_Initialize();
4821
4822 static bool destroyed;
4823 destroyed = false;
4824 destructor dealloc = [](PyObject* self) {
4825 PyTypeObject* type = Py_TYPE(self);
4826 destroyed = true;
4827 PyObject_Del(self);
4828 Py_DECREF(type);
4829 };
4830 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_dealloc, dealloc));
4831
4832 PyTypeObject* type = reinterpret_cast<PyTypeObject*>(mainModuleGet("Bar"));
4833 PyObject* obj = PyObject_New(PyObject, type);
4834 Py_DECREF(type);
4835 ASSERT_EQ(moduleSet("__main__", "bar_obj", obj), 0);
4836 Py_DECREF(obj);
4837
4838 ASSERT_FALSE(destroyed);
4839 Py_FinalizeEx();
4840 ASSERT_TRUE(destroyed);
4841}
4842
4843TEST_F(TypeExtensionApiTest, CallIterSlotFromManagedCode) {
4844 unaryfunc iter_func = [](PyObject* self) {
4845 Py_INCREF(self);
4846 return self;
4847 };
4848 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Foo", Py_tp_iter, iter_func));
4849
4850 ASSERT_EQ(PyRun_SimpleString(R"(
4851f = Foo()
4852itr = f.__iter__()
4853)"),
4854 0);
4855
4856 PyObjectPtr f(mainModuleGet("f"));
4857 PyObjectPtr itr(mainModuleGet("itr"));
4858 EXPECT_EQ(f, itr);
4859}
4860
4861TEST_F(TypeExtensionApiTest, TypeCheckWithSameTypeReturnsTrue) {
4862 PyObjectPtr pylong(PyLong_FromLong(10));
4863 PyObjectPtr pylong_type(PyObject_Type(pylong));
4864 EXPECT_EQ(PyObject_TypeCheck(
4865 pylong, reinterpret_cast<PyTypeObject*>(pylong_type.get())),
4866 1);
4867}
4868
4869TEST_F(TypeExtensionApiTest, TypeCheckWithSubtypeReturnsTrue) {
4870 ASSERT_EQ(PyRun_SimpleString(R"(
4871class MyFloat(float): pass
4872myflt = MyFloat(1.23)
4873)"),
4874 0);
4875 PyObjectPtr myfloat(mainModuleGet("myflt"));
4876 PyObjectPtr pyfloat(PyFloat_FromDouble(3.21));
4877 PyObjectPtr pyfloat_type(PyObject_Type(pyfloat));
4878 EXPECT_EQ(PyObject_TypeCheck(
4879 myfloat, reinterpret_cast<PyTypeObject*>(pyfloat_type.get())),
4880 1);
4881}
4882
4883TEST_F(TypeExtensionApiTest, TypeCheckWithDifferentTypesReturnsFalse) {
4884 PyObjectPtr pylong(PyLong_FromLong(10));
4885 PyObjectPtr pyuni(PyUnicode_FromString("string"));
4886 PyObjectPtr pyuni_type(PyObject_Type(pyuni));
4887 EXPECT_EQ(PyObject_TypeCheck(
4888 pylong, reinterpret_cast<PyTypeObject*>(pyuni_type.get())),
4889 0);
4890}
4891
4892TEST_F(TypeExtensionApiTest, SetDunderClassWithExtensionTypeRaisesTypeError) {
4893 destructor dealloc = [](PyObject* self) {
4894 PyTypeObject* type = Py_TYPE(self);
4895 PyObject_Del(self);
4896 Py_DECREF(type);
4897 };
4898 ASSERT_NO_FATAL_FAILURE(createTypeWithSlot("Bar", Py_tp_dealloc, dealloc));
4899
4900 CaptureStdStreams streams;
4901 PyRun_SimpleString(R"(
4902bar = Bar()
4903class C: pass
4904bar.__class__ = C
4905)");
4906 std::string err = streams.err();
4907 EXPECT_NE(err.find("TypeError:"), std::string::npos);
4908 EXPECT_NE(err.find("__class__"), std::string::npos);
4909 EXPECT_NE(err.find("differs"), std::string::npos);
4910}
4911
4912TEST_F(TypeExtensionApiTest, TpDeallocWithoutFreeingMemoryUntracksNativeProxy) {
4913 // We test this indirectly with a typical freelist pattern; `PyObject_Init`
4914 // only works on memory that has been untracked before.
4915 static const int max_free = 4;
4916 static int numfree = 0;
4917 static PyObject* freelist[max_free];
4918
4919 newfunc new_func = [](PyTypeObject* type, PyObject*, PyObject*) {
4920 PyObject* object;
4921 if (numfree > 0) {
4922 object = freelist[--numfree];
4923 PyObject_Init(object, type);
4924 } else {
4925 object = PyObject_New(PyObject, type);
4926 }
4927 return object;
4928 };
4929 destructor dealloc = [](PyObject* self) {
4930 PyTypeObject* type = Py_TYPE(self);
4931 Py_DECREF(type);
4932 if (numfree + 1 < max_free) {
4933 freelist[numfree++] = self;
4934 } else {
4935 freefunc tp_free =
4936 reinterpret_cast<freefunc>(PyType_GetSlot(type, Py_tp_free));
4937 tp_free(self);
4938 }
4939 };
4940
4941 static const PyType_Slot slots[] = {
4942 {Py_tp_new, reinterpret_cast<void*>(new_func)},
4943 {Py_tp_dealloc, reinterpret_cast<void*>(dealloc)},
4944 {0, nullptr},
4945 };
4946 static const PyType_Spec spec = {
4947 "foo", 0, 0, Py_TPFLAGS_DEFAULT, const_cast<PyType_Slot*>(slots),
4948 };
4949 PyObjectPtr type(PyType_FromSpec(const_cast<PyType_Spec*>(&spec)));
4950 ASSERT_NE(type, nullptr);
4951
4952 PyObject* o0 = _PyObject_CallNoArg(type);
4953 PyObject* o1 = _PyObject_CallNoArg(type);
4954 Py_DECREF(o0);
4955 collectGarbage();
4956 PyObject* o2 = _PyObject_CallNoArg(type);
4957 EXPECT_EQ(o0, o2);
4958 Py_DECREF(o1);
4959 collectGarbage();
4960 PyObject* o3 = _PyObject_CallNoArg(type);
4961 EXPECT_EQ(o1, o3);
4962 PyObject* o4 = _PyObject_CallNoArg(type);
4963 Py_DECREF(o3);
4964 Py_DECREF(o2);
4965 Py_DECREF(o4);
4966}
4967
4968struct TpSlotTestObject {
4969 PyObject_HEAD
4970 int val0;
4971 int val1;
4972};
4973static PyObject* makeTestInstanceWithSlots(const PyType_Slot* slots) {
4974 const PyType_Spec spec = {
4975 "foo",
4976 sizeof(TpSlotTestObject),
4977 0,
4978 Py_TPFLAGS_DEFAULT,
4979 const_cast<PyType_Slot*>(slots),
4980 };
4981 PyObjectPtr type(PyType_FromSpec(const_cast<PyType_Spec*>(&spec)));
4982 if (type == nullptr) return nullptr;
4983 PyObject* instance(PyObject_CallFunction(type, nullptr));
4984 if (instance == nullptr) return nullptr;
4985 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(instance);
4986 data->val0 = 42;
4987 data->val1 = 128077;
4988 return instance;
4989}
4990
4991TEST_F(TypeExtensionApiTest, CallDunderStrReturnsStr) {
4992 reprfunc func = [](PyObject* obj) -> PyObject* {
4993 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
4994 return PyUnicode_FromFormat("<str %d %c>", data->val0, data->val1);
4995 };
4996 static const PyType_Slot slots[] = {
4997 {Py_tp_str, reinterpret_cast<void*>(func)},
4998 {0, nullptr},
4999 };
5000 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5001 ASSERT_NE(instance, nullptr);
5002 PyObjectPtr result(PyObject_CallMethod(instance, "__str__", nullptr));
5003 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<str 42 \xf0\x9f\x91\x8d>"));
5004}
5005
5006TEST_F(TypeExtensionApiTest, CallDunderReprReturnsStr) {
5007 reprfunc func = [](PyObject* obj) -> PyObject* {
5008 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5009 return PyUnicode_FromFormat("<repr %d %c>", data->val0, data->val1);
5010 };
5011 static const PyType_Slot slots[] = {
5012 {Py_tp_repr, reinterpret_cast<void*>(func)},
5013 {0, nullptr},
5014 };
5015 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5016 ASSERT_NE(instance, nullptr);
5017 PyObjectPtr result(PyObject_CallMethod(instance, "__repr__", nullptr));
5018 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<repr 42 \xf0\x9f\x91\x8d>"));
5019}
5020
5021TEST_F(TypeExtensionApiTest, CallDunderIterReturnsStr) {
5022 getiterfunc func = [](PyObject* obj) -> PyObject* {
5023 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5024 return PyUnicode_FromFormat("<iter %d %c>", data->val0, data->val1);
5025 };
5026 static const PyType_Slot slots[] = {
5027 {Py_tp_iter, reinterpret_cast<void*>(func)},
5028 {0, nullptr},
5029 };
5030 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5031 ASSERT_NE(instance, nullptr);
5032 PyObjectPtr result(PyObject_CallMethod(instance, "__iter__", nullptr));
5033 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<iter 42 \xf0\x9f\x91\x8d>"));
5034}
5035
5036TEST_F(TypeExtensionApiTest, CallDunderAwaitReturnsStr) {
5037 reprfunc func = [](PyObject* obj) -> PyObject* {
5038 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5039 return PyUnicode_FromFormat("<await %d %c>", data->val0, data->val1);
5040 };
5041 static const PyType_Slot slots[] = {
5042 {Py_am_await, reinterpret_cast<void*>(func)},
5043 {0, nullptr},
5044 };
5045 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5046 ASSERT_NE(instance, nullptr);
5047 PyObjectPtr result(PyObject_CallMethod(instance, "__await__", nullptr));
5048 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<await 42 \xf0\x9f\x91\x8d>"));
5049}
5050
5051TEST_F(TypeExtensionApiTest, CallDunderAiterReturnsStr) {
5052 reprfunc func = [](PyObject* obj) -> PyObject* {
5053 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5054 return PyUnicode_FromFormat("<aiter %d %c>", data->val0, data->val1);
5055 };
5056 static const PyType_Slot slots[] = {
5057 {Py_am_aiter, reinterpret_cast<void*>(func)},
5058 {0, nullptr},
5059 };
5060 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5061 ASSERT_NE(instance, nullptr);
5062 PyObjectPtr result(PyObject_CallMethod(instance, "__aiter__", nullptr));
5063 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<aiter 42 \xf0\x9f\x91\x8d>"));
5064}
5065
5066TEST_F(TypeExtensionApiTest, CallDunderAnextReturnsStr) {
5067 reprfunc func = [](PyObject* obj) -> PyObject* {
5068 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5069 return PyUnicode_FromFormat("<aiter %d %c>", data->val0, data->val1);
5070 };
5071 static const PyType_Slot slots[] = {
5072 {Py_am_anext, reinterpret_cast<void*>(func)},
5073 {0, nullptr},
5074 };
5075 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5076 ASSERT_NE(instance, nullptr);
5077 PyObjectPtr result(PyObject_CallMethod(instance, "__anext__", nullptr));
5078 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<aiter 42 \xf0\x9f\x91\x8d>"));
5079}
5080
5081TEST_F(TypeExtensionApiTest, CallDunderNegReturnsStr) {
5082 reprfunc func = [](PyObject* obj) -> PyObject* {
5083 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5084 return PyUnicode_FromFormat("<neg %d %c>", data->val0, data->val1);
5085 };
5086 static const PyType_Slot slots[] = {
5087 {Py_nb_negative, reinterpret_cast<void*>(func)},
5088 {0, nullptr},
5089 };
5090 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5091 ASSERT_NE(instance, nullptr);
5092 PyObjectPtr result(PyObject_CallMethod(instance, "__neg__", nullptr));
5093 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<neg 42 \xf0\x9f\x91\x8d>"));
5094}
5095
5096TEST_F(TypeExtensionApiTest, CallDunderPosReturnsStr) {
5097 reprfunc func = [](PyObject* obj) -> PyObject* {
5098 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5099 return PyUnicode_FromFormat("<pos %d %c>", data->val0, data->val1);
5100 };
5101 static const PyType_Slot slots[] = {
5102 {Py_nb_positive, reinterpret_cast<void*>(func)},
5103 {0, nullptr},
5104 };
5105 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5106 ASSERT_NE(instance, nullptr);
5107 PyObjectPtr result(PyObject_CallMethod(instance, "__pos__", nullptr));
5108 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<pos 42 \xf0\x9f\x91\x8d>"));
5109}
5110
5111TEST_F(TypeExtensionApiTest, CallDunderAbsReturnsStr) {
5112 reprfunc func = [](PyObject* obj) -> PyObject* {
5113 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5114 return PyUnicode_FromFormat("<abs %d %c>", data->val0, data->val1);
5115 };
5116 static const PyType_Slot slots[] = {
5117 {Py_nb_absolute, reinterpret_cast<void*>(func)},
5118 {0, nullptr},
5119 };
5120 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5121 ASSERT_NE(instance, nullptr);
5122 PyObjectPtr result(PyObject_CallMethod(instance, "__abs__", nullptr));
5123 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<abs 42 \xf0\x9f\x91\x8d>"));
5124}
5125
5126TEST_F(TypeExtensionApiTest, CallDunderInvertReturnsStr) {
5127 reprfunc func = [](PyObject* obj) -> PyObject* {
5128 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5129 return PyUnicode_FromFormat("<invert %d %c>", data->val0, data->val1);
5130 };
5131 static const PyType_Slot slots[] = {
5132 {Py_nb_invert, reinterpret_cast<void*>(func)},
5133 {0, nullptr},
5134 };
5135 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5136 ASSERT_NE(instance, nullptr);
5137 PyObjectPtr result(PyObject_CallMethod(instance, "__invert__", nullptr));
5138 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<invert 42 \xf0\x9f\x91\x8d>"));
5139}
5140
5141TEST_F(TypeExtensionApiTest, CallDunderIntReturnsStr) {
5142 reprfunc func = [](PyObject* obj) -> PyObject* {
5143 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5144 return PyUnicode_FromFormat("<int %d %c>", data->val0, data->val1);
5145 };
5146 static const PyType_Slot slots[] = {
5147 {Py_nb_int, reinterpret_cast<void*>(func)},
5148 {0, nullptr},
5149 };
5150 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5151 ASSERT_NE(instance, nullptr);
5152 PyObjectPtr result(PyObject_CallMethod(instance, "__int__", nullptr));
5153 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<int 42 \xf0\x9f\x91\x8d>"));
5154}
5155
5156TEST_F(TypeExtensionApiTest, CallDunderFloatReturnsStr) {
5157 reprfunc func = [](PyObject* obj) -> PyObject* {
5158 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5159 return PyUnicode_FromFormat("<float %d %c>", data->val0, data->val1);
5160 };
5161 static const PyType_Slot slots[] = {
5162 {Py_nb_float, reinterpret_cast<void*>(func)},
5163 {0, nullptr},
5164 };
5165 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5166 ASSERT_NE(instance, nullptr);
5167 PyObjectPtr result(PyObject_CallMethod(instance, "__float__", nullptr));
5168 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<float 42 \xf0\x9f\x91\x8d>"));
5169}
5170
5171TEST_F(TypeExtensionApiTest, CallDunderIndexReturnsStr) {
5172 reprfunc func = [](PyObject* obj) -> PyObject* {
5173 TpSlotTestObject* data = reinterpret_cast<TpSlotTestObject*>(obj);
5174 return PyUnicode_FromFormat("<index %d %c>", data->val0, data->val1);
5175 };
5176 static const PyType_Slot slots[] = {
5177 {Py_nb_index, reinterpret_cast<void*>(func)},
5178 {0, nullptr},
5179 };
5180 PyObjectPtr instance(makeTestInstanceWithSlots(slots));
5181 ASSERT_NE(instance, nullptr);
5182 PyObjectPtr result(PyObject_CallMethod(instance, "__index__", nullptr));
5183 EXPECT_TRUE(isUnicodeEqualsCStr(result, "<index 42 \xf0\x9f\x91\x8d>"));
5184}
5185
5186TEST_F(TypeExtensionApiTest, MultipleInheritanceWithBaseWithoutSlotsWorks) {
5187 PyType_Slot slots[] = {
5188 {0, nullptr},
5189 };
5190 static PyType_Spec spec;
5191 int size = sizeof(PyObject) + 17;
5192 spec = {
5193 "foo.N", size, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots,
5194 };
5195 PyObjectPtr ext_type(PyType_FromSpec(&spec));
5196 moduleSet("__main__", "N", ext_type);
5197 EXPECT_EQ(PyRun_SimpleString(R"(
5198class A:
5199 pass
5200class C(A, N):
5201 pass
5202class D(C):
5203 pass
5204c = C()
5205d = D()
5206 )"),
5207 0);
5208 PyObjectPtr a(mainModuleGet("A"));
5209 PyObjectPtr c(mainModuleGet("C"));
5210 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(c.get())) &
5211 Py_TPFLAGS_DEFAULT);
5212 EXPECT_TRUE(PyType_GetFlags(reinterpret_cast<PyTypeObject*>(c.get())) &
5213 Py_TPFLAGS_BASETYPE);
5214 PyObjectPtr c_size(PyObject_GetAttrString(c.get(), "__basicsize__"));
5215 EXPECT_TRUE(PyLong_AsLong(c_size) >= size);
5216
5217 PyObjectPtr mro(PyObject_GetAttrString(c.get(), "__mro__"));
5218 EXPECT_TRUE(PyTuple_Check(mro));
5219 EXPECT_EQ(PyTuple_GetItem(mro, 0), c.get());
5220 EXPECT_EQ(PyTuple_GetItem(mro, 1), a.get());
5221 EXPECT_EQ(PyTuple_GetItem(mro, 2), ext_type.get());
5222
5223 PyObjectPtr base(PyObject_GetAttrString(c.get(), "__base__"));
5224 EXPECT_EQ(base.get(), ext_type.get());
5225}
5226
5227struct TpSlotRefcntTestObject {
5228 PyObject_HEAD
5229 Py_ssize_t initial_refcnt;
5230};
5231
5232static PyObject* makeTestRefcntInstanceWithSlots(const PyType_Slot* slots) {
5233 const PyType_Spec spec = {
5234 "foo",
5235 sizeof(TpSlotRefcntTestObject),
5236 0,
5237 Py_TPFLAGS_DEFAULT,
5238 const_cast<PyType_Slot*>(slots),
5239 };
5240 PyObjectPtr type(PyType_FromSpec(const_cast<PyType_Spec*>(&spec)));
5241 if (type == nullptr) return nullptr;
5242 PyObject* instance = PyObject_CallFunction(type, nullptr);
5243 if (instance == nullptr) return nullptr;
5244 reinterpret_cast<TpSlotRefcntTestObject*>(instance)->initial_refcnt =
5245 Py_REFCNT(instance);
5246 return instance;
5247}
5248
5249TEST_F(TypeExtensionApiTest, IntegralSlotOwnsReference) {
5250 lenfunc len_func = [](PyObject* self) -> Py_ssize_t {
5251 // We expect a refcount of 1 greater than the inital refcount since the
5252 // method is called with a new reference.
5253 EXPECT_EQ(
5254 Py_REFCNT(self),
5255 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5256 return 0xdeadbeef;
5257 };
5258 // __len__ is created as an integral slot
5259 static const PyType_Slot slots[] = {
5260 {Py_sq_length, reinterpret_cast<void*>(len_func)},
5261 {0, nullptr},
5262 };
5263 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5264 ASSERT_NE(instance, nullptr);
5265 PyObjectPtr result(PyObject_CallMethod(instance, "__len__", nullptr));
5266 // Once the call is complete the wrapper should decref the arg so we expect to
5267 // see the original refcount.
5268 EXPECT_EQ(Py_REFCNT(instance),
5269 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5270 ->initial_refcnt);
5271 EXPECT_TRUE(isLongEqualsLong(result, 0xdeadbeef));
5272}
5273
5274TEST_F(TypeExtensionApiTest, UnarySlotOwnsReference) {
5275 reprfunc func = [](PyObject* obj) -> PyObject* {
5276 // We expect a refcount of 1 greater than the inital refcount since the
5277 // method is called with a new reference.
5278 EXPECT_EQ(
5279 Py_REFCNT(obj),
5280 reinterpret_cast<TpSlotRefcntTestObject*>(obj)->initial_refcnt + 1);
5281 Py_RETURN_NONE;
5282 };
5283 // __repr__ is created as a unary slot
5284 static const PyType_Slot slots[] = {
5285 {Py_tp_repr, reinterpret_cast<void*>(func)},
5286 {0, nullptr},
5287 };
5288 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5289 ASSERT_NE(instance, nullptr);
5290 PyObjectPtr result(PyObject_CallMethod(instance, "__repr__", nullptr));
5291 // Once the call is complete the wrapper should decref the arg so we expect to
5292 // see the original refcount.
5293 EXPECT_EQ(Py_REFCNT(instance),
5294 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5295 ->initial_refcnt);
5296 EXPECT_EQ(result, Py_None);
5297}
5298
5299TEST_F(TypeExtensionApiTest, BinarySlotOwnsReference) {
5300 binaryfunc func = [](PyObject* self, PyObject* other) -> PyObject* {
5301 // We expect a refcount of 1 greater than the inital refcount since the
5302 // method is called with a new reference.
5303 EXPECT_EQ(
5304 Py_REFCNT(self),
5305 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5306 // TODO(T89073278): Revert to EQ(other, initial+1).
5307 EXPECT_GE(
5308 Py_REFCNT(other),
5309 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5310 Py_RETURN_NONE;
5311 };
5312 // __add__ is created as a binary slot
5313 static const PyType_Slot slots[] = {
5314 {Py_nb_add, reinterpret_cast<void*>(func)},
5315 {0, nullptr},
5316 };
5317 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5318 ASSERT_NE(instance, nullptr);
5319 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5320 ASSERT_NE(other, nullptr);
5321 PyObjectPtr result(
5322 PyObject_CallMethod(instance, "__add__", "O", other.get()));
5323 // Once the call is complete the wrapper should decref the arg so we expect to
5324 // see the original refcount.
5325 EXPECT_EQ(Py_REFCNT(instance),
5326 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5327 ->initial_refcnt);
5328 EXPECT_EQ(
5329 Py_REFCNT(other),
5330 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5331 EXPECT_EQ(result, Py_None);
5332}
5333
5334TEST_F(TypeExtensionApiTest, BinarySwappedSlotOwnsReference) {
5335 binaryfunc func = [](PyObject* self, PyObject* other) -> PyObject* {
5336 // We expect a refcount of 1 greater than the inital refcount since the
5337 // method is called with a new reference.
5338 // TODO(T89073278): Revert to EQ(other, initial+1).
5339 EXPECT_GE(
5340 Py_REFCNT(self),
5341 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5342 EXPECT_EQ(
5343 Py_REFCNT(other),
5344 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5345 Py_RETURN_NONE;
5346 };
5347 // __add__ is created as a binary slot
5348 static const PyType_Slot slots[] = {
5349 {Py_nb_add, reinterpret_cast<void*>(func)},
5350 {0, nullptr},
5351 };
5352 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5353 ASSERT_NE(instance, nullptr);
5354 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5355 ASSERT_NE(other, nullptr);
5356 PyObjectPtr result(
5357 PyObject_CallMethod(instance, "__radd__", "O", other.get()));
5358 // Once the call is complete the wrapper should decref the arg so we expect to
5359 // see the original refcount.
5360 EXPECT_EQ(Py_REFCNT(instance),
5361 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5362 ->initial_refcnt);
5363 EXPECT_EQ(
5364 Py_REFCNT(other),
5365 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5366 EXPECT_EQ(result, Py_None);
5367}
5368
5369TEST_F(TypeExtensionApiTest, TernarySlotOwnsReference) {
5370 ternaryfunc func = [](PyObject* self, PyObject* other,
5371 PyObject* third) -> PyObject* {
5372 // We expect a refcount of 1 greater than the inital refcount since the
5373 // method is called with a new reference.
5374 EXPECT_EQ(
5375 Py_REFCNT(self),
5376 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5377 // TODO(T89073278): Revert to EQ(other, initial+1).
5378 EXPECT_GE(
5379 Py_REFCNT(other),
5380 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5381 // TODO(T89073278): Revert to EQ(other, initial+1).
5382 EXPECT_GE(
5383 Py_REFCNT(third),
5384 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5385 Py_RETURN_NONE;
5386 };
5387 // __pow__ is created as a ternary slot
5388 static const PyType_Slot slots[] = {
5389 {Py_nb_power, reinterpret_cast<void*>(func)},
5390 {0, nullptr},
5391 };
5392 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5393 ASSERT_NE(instance, nullptr);
5394 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5395 ASSERT_NE(other, nullptr);
5396 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5397 ASSERT_NE(third, nullptr);
5398 PyObjectPtr result(
5399 PyObject_CallMethod(instance, "__pow__", "OO", other.get(), third.get()));
5400 // Once the call is complete the wrapper should decref the arg so we expect to
5401 // see the original refcount.
5402 EXPECT_EQ(Py_REFCNT(instance),
5403 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5404 ->initial_refcnt);
5405 EXPECT_EQ(
5406 Py_REFCNT(other),
5407 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5408 EXPECT_EQ(
5409 Py_REFCNT(third),
5410 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5411 EXPECT_EQ(result, Py_None);
5412}
5413
5414TEST_F(TypeExtensionApiTest, TernarySwappedSlotOwnsReference) {
5415 ternaryfunc func = [](PyObject* self, PyObject* other,
5416 PyObject* third) -> PyObject* {
5417 // We expect a refcount of 1 greater than the inital refcount since the
5418 // method is called with a new reference.
5419 // TODO(T89073278): Revert to EQ(other, initial+1).
5420 EXPECT_GE(
5421 Py_REFCNT(self),
5422 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5423 EXPECT_EQ(
5424 Py_REFCNT(other),
5425 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5426 // TODO(T89073278): Revert to EQ(other, initial+1).
5427 EXPECT_GE(
5428 Py_REFCNT(third),
5429 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5430 Py_RETURN_NONE;
5431 };
5432 // __pow__ is created as a ternary slot
5433 static const PyType_Slot slots[] = {
5434 {Py_nb_power, reinterpret_cast<void*>(func)},
5435 {0, nullptr},
5436 };
5437 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5438 ASSERT_NE(instance, nullptr);
5439 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5440 ASSERT_NE(other, nullptr);
5441 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5442 ASSERT_NE(third, nullptr);
5443 PyObjectPtr result(PyObject_CallMethod(instance, "__rpow__", "OO",
5444 other.get(), third.get()));
5445 // Once the call is complete the wrapper should decref the arg so we expect to
5446 // see the original refcount.
5447 EXPECT_EQ(Py_REFCNT(instance),
5448 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5449 ->initial_refcnt);
5450 EXPECT_EQ(
5451 Py_REFCNT(other),
5452 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5453 EXPECT_EQ(
5454 Py_REFCNT(third),
5455 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5456 EXPECT_EQ(result, Py_None);
5457}
5458
5459TEST_F(TypeExtensionApiTest, TernaryVarKwSlotOwnsReference) {
5460 ternaryfunc func = [](PyObject* self, PyObject* other,
5461 PyObject* third) -> PyObject* {
5462 // We expect a refcount of 1 greater than the inital refcount since the
5463 // method is called with a new reference.
5464 EXPECT_EQ(
5465 Py_REFCNT(self),
5466 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5467 EXPECT_EQ(
5468 Py_REFCNT(other),
5469 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5470 EXPECT_EQ(
5471 Py_REFCNT(third),
5472 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5473 Py_RETURN_NONE;
5474 };
5475 // __pow__ is created as a ternary slot
5476 static const PyType_Slot slots[] = {
5477 {Py_nb_power, reinterpret_cast<void*>(func)},
5478 {0, nullptr},
5479 };
5480 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5481 ASSERT_NE(instance, nullptr);
5482 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5483 ASSERT_NE(other, nullptr);
5484 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5485 ASSERT_NE(third, nullptr);
5486 PyObjectPtr args(PyTuple_Pack(2, other.get(), third.get()));
5487 PyObjectPtr pow(PyObject_GetAttrString(instance, "__pow__"));
5488 PyObjectPtr result(PyObject_Call(pow, args, nullptr));
5489 // Once the call is complete the wrapper should decref the arg so we expect to
5490 // see the original refcount.
5491 // TODO(T89073278): Revert to EQ(other, initial+1).
5492 EXPECT_GE(Py_REFCNT(instance),
5493 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5494 ->initial_refcnt);
5495 // TODO(T89073278): Revert to EQ(other, initial+1).
5496 EXPECT_GE(
5497 Py_REFCNT(other),
5498 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5499 // TODO(T89073278): Revert to EQ(other, initial+1).
5500 EXPECT_GE(
5501 Py_REFCNT(third),
5502 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5503 EXPECT_EQ(result, Py_None);
5504}
5505
5506TEST_F(TypeExtensionApiTest, SetattrWrapperOwnsReference) {
5507 setattrofunc func = [](PyObject* self, PyObject*, PyObject* third) -> int {
5508 // We expect a refcount of 1 greater than the inital refcount since the
5509 // method is called with a new reference.
5510 // TODO(T89073278): Revert to EQ(other, initial+1).
5511 EXPECT_LE(
5512 Py_REFCNT(self),
5513 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5514 // The middle arg is not checked because it needs to be a string, not
5515 // whatever object we use to hold a refcount.
5516 // TODO(T89073278): Revert to EQ(other, initial+1).
5517 EXPECT_LE(
5518 Py_REFCNT(third),
5519 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5520 return 0;
5521 };
5522 static const PyType_Slot slots[] = {
5523 {Py_tp_setattro, reinterpret_cast<void*>(func)},
5524 {0, nullptr},
5525 };
5526 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5527 ASSERT_NE(instance, nullptr);
5528 PyObjectPtr other(PyUnicode_FromString("name"));
5529 ASSERT_NE(other, nullptr);
5530 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5531 ASSERT_NE(third, nullptr);
5532 int result = PyObject_SetAttr(instance, other, third);
5533 EXPECT_EQ(result, 0);
5534 // Once the call is complete the wrapper should decref the arg so we expect to
5535 // see the original refcount.
5536 EXPECT_EQ(Py_REFCNT(instance),
5537 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5538 ->initial_refcnt);
5539 // The middle arg is not checked because it needs to be a string, not
5540 // whatever object we use to hold a refcount.
5541 EXPECT_EQ(
5542 Py_REFCNT(third),
5543 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5544}
5545
5546TEST_F(TypeExtensionApiTest, DelattrWrapperOwnsReference) {
5547 setattrofunc func = [](PyObject* self, PyObject*, PyObject*) -> int {
5548 // We expect a refcount of 1 greater than the inital refcount since the
5549 // method is called with a new reference.
5550 // TODO(T89073278): Revert to EQ(other, initial+1).
5551 EXPECT_LE(
5552 Py_REFCNT(self),
5553 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5554 // The second arg is not checked because it needs to be a string, not
5555 // whatever object we use to hold a refcount.
5556 return 0;
5557 };
5558 static const PyType_Slot slots[] = {
5559 {Py_tp_setattro, reinterpret_cast<void*>(func)},
5560 {0, nullptr},
5561 };
5562 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5563 ASSERT_NE(instance, nullptr);
5564 PyObjectPtr other(PyUnicode_FromString("name"));
5565 ASSERT_NE(other, nullptr);
5566 // Need to set attribute before we can delete it.
5567 PyObjectPtr value(PyLong_FromLong(5));
5568 int result = PyObject_SetAttr(instance, other, value);
5569 EXPECT_EQ(result, 0);
5570 result = PyObject_DelAttr(instance, other);
5571 EXPECT_EQ(result, 0);
5572 // Once the call is complete the wrapper should decref the arg so we expect to
5573 // see the original refcount.
5574 EXPECT_EQ(Py_REFCNT(instance),
5575 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5576 ->initial_refcnt);
5577 // The second arg is not checked because it needs to be a string, not
5578 // whatever object we use to hold a refcount.
5579}
5580
5581TEST_F(TypeExtensionApiTest, RichCmpSlotOwnsReference) {
5582 richcmpfunc func = [](PyObject* self, PyObject* other, int) -> PyObject* {
5583 // We expect a refcount of 1 greater than the inital refcount since the
5584 // method is called with a new reference.
5585 EXPECT_EQ(
5586 Py_REFCNT(self),
5587 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5588 // TODO(T89073278): Revert to EQ(other, initial+1).
5589 EXPECT_GE(
5590 Py_REFCNT(other),
5591 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5592 Py_RETURN_TRUE;
5593 };
5594 static const PyType_Slot slots[] = {
5595 {Py_tp_richcompare, reinterpret_cast<void*>(func)},
5596 {0, nullptr},
5597 };
5598 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5599 ASSERT_NE(instance, nullptr);
5600 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5601 ASSERT_NE(other, nullptr);
5602 PyObjectPtr result(PyObject_CallMethod(instance, "__eq__", "O", other.get()));
5603 // Once the call is complete the wrapper should decref the arg so we expect to
5604 // see the original refcount.
5605 EXPECT_EQ(Py_REFCNT(instance),
5606 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5607 ->initial_refcnt);
5608 EXPECT_EQ(
5609 Py_REFCNT(other),
5610 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5611 EXPECT_EQ(result, Py_True);
5612}
5613
5614TEST_F(TypeExtensionApiTest, NextSlotOwnsReference) {
5615 unaryfunc func = [](PyObject* obj) -> PyObject* {
5616 // We expect a refcount of 1 greater than the inital refcount since the
5617 // method is called with a new reference.
5618 EXPECT_EQ(
5619 Py_REFCNT(obj),
5620 reinterpret_cast<TpSlotRefcntTestObject*>(obj)->initial_refcnt + 1);
5621 Py_RETURN_NONE;
5622 };
5623 static const PyType_Slot slots[] = {
5624 {Py_tp_iternext, reinterpret_cast<void*>(func)},
5625 {0, nullptr},
5626 };
5627 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5628 ASSERT_NE(instance, nullptr);
5629 PyObjectPtr result(PyObject_CallMethod(instance, "__next__", nullptr));
5630 // Once the call is complete the wrapper should decref the arg so we expect to
5631 // see the original refcount.
5632 EXPECT_EQ(Py_REFCNT(instance),
5633 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5634 ->initial_refcnt);
5635 EXPECT_EQ(result, Py_None);
5636}
5637
5638TEST_F(TypeExtensionApiTest, DescrGetSlotOwnsReference) {
5639 descrgetfunc func = [](PyObject* self, PyObject* other,
5640 PyObject* third) -> PyObject* {
5641 // We expect a refcount of 1 greater than the inital refcount since the
5642 // method is called with a new reference.
5643 EXPECT_EQ(
5644 Py_REFCNT(self),
5645 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5646 // TODO(T89073278): Revert to EQ(other, initial+1).
5647 EXPECT_GE(
5648 Py_REFCNT(other),
5649 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5650 // TODO(T89073278): Revert to EQ(other, initial+1).
5651 EXPECT_GE(
5652 Py_REFCNT(third),
5653 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5654 Py_RETURN_NONE;
5655 };
5656 static const PyType_Slot slots[] = {
5657 {Py_tp_descr_get, reinterpret_cast<void*>(func)},
5658 {0, nullptr},
5659 };
5660 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5661 ASSERT_NE(instance, nullptr);
5662 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5663 ASSERT_NE(other, nullptr);
5664 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5665 ASSERT_NE(third, nullptr);
5666 PyObjectPtr result(
5667 PyObject_CallMethod(instance, "__get__", "OO", other.get(), third.get()));
5668 // Once the call is complete the wrapper should decref the arg so we expect to
5669 // see the original refcount.
5670 EXPECT_EQ(Py_REFCNT(instance),
5671 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5672 ->initial_refcnt);
5673 EXPECT_EQ(
5674 Py_REFCNT(other),
5675 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5676 EXPECT_EQ(
5677 Py_REFCNT(third),
5678 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5679 EXPECT_EQ(result, Py_None);
5680}
5681
5682TEST_F(TypeExtensionApiTest, DescrSetSlotOwnsReference) {
5683 descrsetfunc func = [](PyObject* self, PyObject* other,
5684 PyObject* third) -> int {
5685 // We expect a refcount of 1 greater than the inital refcount since the
5686 // method is called with a new reference.
5687 EXPECT_EQ(
5688 Py_REFCNT(self),
5689 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5690 // TODO(T89073278): Revert to EQ(other, initial+1).
5691 EXPECT_GE(
5692 Py_REFCNT(other),
5693 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5694 // TODO(T89073278): Revert to EQ(other, initial+1).
5695 EXPECT_GE(
5696 Py_REFCNT(third),
5697 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5698 return 0;
5699 };
5700 static const PyType_Slot slots[] = {
5701 {Py_tp_descr_set, reinterpret_cast<void*>(func)},
5702 {0, nullptr},
5703 };
5704 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5705 ASSERT_NE(instance, nullptr);
5706 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5707 ASSERT_NE(other, nullptr);
5708 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5709 ASSERT_NE(third, nullptr);
5710 PyObjectPtr result(
5711 PyObject_CallMethod(instance, "__set__", "OO", other.get(), third.get()));
5712 // Once the call is complete the wrapper should decref the arg so we expect to
5713 // see the original refcount.
5714 EXPECT_EQ(Py_REFCNT(instance),
5715 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5716 ->initial_refcnt);
5717 EXPECT_EQ(
5718 Py_REFCNT(other),
5719 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5720 EXPECT_EQ(
5721 Py_REFCNT(third),
5722 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5723 EXPECT_EQ(result, Py_None);
5724}
5725
5726TEST_F(TypeExtensionApiTest, DescrDeleteSlotOwnsReference) {
5727 descrsetfunc func = [](PyObject* self, PyObject* other,
5728 PyObject* third) -> int {
5729 // We expect a refcount of 1 greater than the inital refcount since the
5730 // method is called with a new reference.
5731 EXPECT_EQ(
5732 Py_REFCNT(self),
5733 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5734 // TODO(T89073278): Revert to EQ(other, initial+1).
5735 EXPECT_GE(
5736 Py_REFCNT(other),
5737 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5738 EXPECT_EQ(third, nullptr);
5739 return 0;
5740 };
5741 static const PyType_Slot slots[] = {
5742 {Py_tp_descr_set, reinterpret_cast<void*>(func)},
5743 {0, nullptr},
5744 };
5745 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5746 ASSERT_NE(instance, nullptr);
5747 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5748 ASSERT_NE(other, nullptr);
5749 PyObjectPtr result(
5750 PyObject_CallMethod(instance, "__delete__", "O", other.get()));
5751 EXPECT_EQ(result, Py_None);
5752 // Once the call is complete the wrapper should decref the arg so we expect to
5753 // see the original refcount.
5754 EXPECT_EQ(Py_REFCNT(instance),
5755 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5756 ->initial_refcnt);
5757 EXPECT_EQ(
5758 Py_REFCNT(other),
5759 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5760}
5761
5762TEST_F(TypeExtensionApiTest, SetitemSlotOwnsReference) {
5763 objobjargproc func = [](PyObject* self, PyObject* other,
5764 PyObject* third) -> int {
5765 // We expect a refcount of 1 greater than the inital refcount since the
5766 // method is called with a new reference.
5767 EXPECT_EQ(
5768 Py_REFCNT(self),
5769 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5770 // TODO(T89073278): Revert to EQ(other, initial+1).
5771 EXPECT_GE(
5772 Py_REFCNT(other),
5773 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5774 // TODO(T89073278): Revert to EQ(other, initial+1).
5775 EXPECT_GE(
5776 Py_REFCNT(third),
5777 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5778 return 0;
5779 };
5780 static const PyType_Slot slots[] = {
5781 {Py_mp_ass_subscript, reinterpret_cast<void*>(func)},
5782 {0, nullptr},
5783 };
5784 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5785 ASSERT_NE(instance, nullptr);
5786 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5787 ASSERT_NE(other, nullptr);
5788 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5789 ASSERT_NE(third, nullptr);
5790 PyObjectPtr result(PyObject_CallMethod(instance, "__setitem__", "OO",
5791 other.get(), third.get()));
5792 EXPECT_EQ(result, Py_None);
5793 // Once the call is complete the wrapper should decref the arg so we expect to
5794 // see the original refcount.
5795 EXPECT_EQ(Py_REFCNT(instance),
5796 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5797 ->initial_refcnt);
5798 EXPECT_EQ(
5799 Py_REFCNT(other),
5800 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5801 EXPECT_EQ(
5802 Py_REFCNT(third),
5803 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5804}
5805
5806TEST_F(TypeExtensionApiTest, DelitemSlotOwnsReference) {
5807 objobjargproc func = [](PyObject* self, PyObject* other,
5808 PyObject* third) -> int {
5809 // We expect a refcount of 1 greater than the inital refcount since the
5810 // method is called with a new reference.
5811 EXPECT_EQ(
5812 Py_REFCNT(self),
5813 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5814 // TODO(T89073278): Revert to EQ(other, initial+1).
5815 EXPECT_GE(
5816 Py_REFCNT(other),
5817 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5818 EXPECT_EQ(third, nullptr);
5819 return 0;
5820 };
5821 static const PyType_Slot slots[] = {
5822 {Py_mp_ass_subscript, reinterpret_cast<void*>(func)},
5823 {0, nullptr},
5824 };
5825 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5826 ASSERT_NE(instance, nullptr);
5827 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5828 ASSERT_NE(other, nullptr);
5829 PyObjectPtr result(
5830 PyObject_CallMethod(instance, "__delitem__", "O", other.get()));
5831 EXPECT_EQ(result, Py_None);
5832 // Once the call is complete the wrapper should decref the arg so we expect to
5833 // see the original refcount.
5834 EXPECT_EQ(Py_REFCNT(instance),
5835 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5836 ->initial_refcnt);
5837 EXPECT_EQ(
5838 Py_REFCNT(other),
5839 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5840}
5841
5842TEST_F(TypeExtensionApiTest, ContainsSlotOwnsReference) {
5843 objobjproc func = [](PyObject* self, PyObject* other) -> int {
5844 // We expect a refcount of 1 greater than the inital refcount since the
5845 // method is called with a new reference.
5846 EXPECT_EQ(
5847 Py_REFCNT(self),
5848 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5849 // TODO(T89073278): Revert to EQ(other, initial+1).
5850 EXPECT_GE(
5851 Py_REFCNT(other),
5852 reinterpret_cast<TpSlotRefcntTestObject*>(other)->initial_refcnt + 1);
5853 return 1;
5854 };
5855 static const PyType_Slot slots[] = {
5856 {Py_sq_contains, reinterpret_cast<void*>(func)},
5857 {0, nullptr},
5858 };
5859 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5860 ASSERT_NE(instance, nullptr);
5861 PyObjectPtr other(makeTestRefcntInstanceWithSlots(slots));
5862 ASSERT_NE(other, nullptr);
5863 PyObjectPtr result(
5864 PyObject_CallMethod(instance, "__contains__", "O", other.get()));
5865 EXPECT_EQ(result, Py_True);
5866 // Once the call is complete the wrapper should decref the arg so we expect to
5867 // see the original refcount.
5868 EXPECT_EQ(Py_REFCNT(instance),
5869 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5870 ->initial_refcnt);
5871 EXPECT_EQ(
5872 Py_REFCNT(other),
5873 reinterpret_cast<TpSlotRefcntTestObject*>(other.get())->initial_refcnt);
5874}
5875
5876TEST_F(TypeExtensionApiTest, MulSlotOwnsReference) {
5877 ssizeargfunc func = [](PyObject* self, Py_ssize_t) -> PyObject* {
5878 // We expect a refcount of 1 greater than the inital refcount since the
5879 // method is called with a new reference.
5880 EXPECT_EQ(
5881 Py_REFCNT(self),
5882 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5883 Py_RETURN_NONE;
5884 };
5885 static const PyType_Slot slots[] = {
5886 {Py_sq_repeat, reinterpret_cast<void*>(func)},
5887 {0, nullptr},
5888 };
5889 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5890 ASSERT_NE(instance, nullptr);
5891 PyObjectPtr other(PyLong_FromLong(5));
5892 ASSERT_NE(other, nullptr);
5893 PyObjectPtr result(
5894 PyObject_CallMethod(instance, "__mul__", "O", other.get()));
5895 EXPECT_EQ(result, Py_None);
5896 // Once the call is complete the wrapper should decref the arg so we expect to
5897 // see the original refcount.
5898 EXPECT_EQ(Py_REFCNT(instance),
5899 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5900 ->initial_refcnt);
5901}
5902
5903TEST_F(TypeExtensionApiTest, SequenceItemSlotOwnsReference) {
5904 ssizeargfunc func = [](PyObject* self, Py_ssize_t) -> PyObject* {
5905 // We expect a refcount of 1 greater than the inital refcount since the
5906 // method is called with a new reference.
5907 EXPECT_EQ(
5908 Py_REFCNT(self),
5909 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5910 Py_RETURN_NONE;
5911 };
5912 static const PyType_Slot slots[] = {
5913 {Py_sq_item, reinterpret_cast<void*>(func)},
5914 {0, nullptr},
5915 };
5916 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5917 ASSERT_NE(instance, nullptr);
5918 PyObjectPtr other(PyLong_FromLong(5));
5919 ASSERT_NE(other, nullptr);
5920 PyObjectPtr result(
5921 PyObject_CallMethod(instance, "__getitem__", "O", other.get()));
5922 EXPECT_EQ(result, Py_None);
5923 // Once the call is complete the wrapper should decref the arg so we expect to
5924 // see the original refcount.
5925 EXPECT_EQ(Py_REFCNT(instance),
5926 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5927 ->initial_refcnt);
5928}
5929
5930TEST_F(TypeExtensionApiTest, SequenceSetItemSlotOwnsReference) {
5931 ssizeobjargproc func = [](PyObject* self, Py_ssize_t,
5932 PyObject* third) -> int {
5933 // We expect a refcount of 1 greater than the inital refcount since the
5934 // method is called with a new reference.
5935 EXPECT_EQ(
5936 Py_REFCNT(self),
5937 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5938 // TODO(T89073278): Revert to EQ(other, initial+1).
5939 EXPECT_GE(
5940 Py_REFCNT(third),
5941 reinterpret_cast<TpSlotRefcntTestObject*>(third)->initial_refcnt + 1);
5942 return 0;
5943 };
5944 static const PyType_Slot slots[] = {
5945 {Py_sq_ass_item, reinterpret_cast<void*>(func)},
5946 {0, nullptr},
5947 };
5948 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5949 ASSERT_NE(instance, nullptr);
5950 PyObjectPtr other(PyLong_FromLong(5));
5951 ASSERT_NE(other, nullptr);
5952 PyObjectPtr third(makeTestRefcntInstanceWithSlots(slots));
5953 ASSERT_NE(third, nullptr);
5954 PyObjectPtr result(PyObject_CallMethod(instance, "__setitem__", "OO",
5955 other.get(), third.get()));
5956 EXPECT_EQ(result, Py_None);
5957 // Once the call is complete the wrapper should decref the arg so we expect to
5958 // see the original refcount.
5959 EXPECT_EQ(Py_REFCNT(instance),
5960 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5961 ->initial_refcnt);
5962 EXPECT_EQ(
5963 Py_REFCNT(third),
5964 reinterpret_cast<TpSlotRefcntTestObject*>(third.get())->initial_refcnt);
5965}
5966
5967TEST_F(TypeExtensionApiTest, SequenceDelItemSlotOwnsReference) {
5968 ssizeobjargproc func = [](PyObject* self, Py_ssize_t,
5969 PyObject* third) -> int {
5970 // We expect a refcount of 1 greater than the inital refcount since the
5971 // method is called with a new reference.
5972 EXPECT_EQ(
5973 Py_REFCNT(self),
5974 reinterpret_cast<TpSlotRefcntTestObject*>(self)->initial_refcnt + 1);
5975 EXPECT_EQ(third, nullptr);
5976 return 0;
5977 };
5978 static const PyType_Slot slots[] = {
5979 {Py_sq_ass_item, reinterpret_cast<void*>(func)},
5980 {0, nullptr},
5981 };
5982 PyObjectPtr instance(makeTestRefcntInstanceWithSlots(slots));
5983 ASSERT_NE(instance, nullptr);
5984 PyObjectPtr other(PyLong_FromLong(5));
5985 ASSERT_NE(other, nullptr);
5986 PyObjectPtr result(
5987 PyObject_CallMethod(instance, "__delitem__", "O", other.get()));
5988 EXPECT_EQ(result, Py_None);
5989 // Once the call is complete the wrapper should decref the arg so we expect to
5990 // see the original refcount.
5991 EXPECT_EQ(Py_REFCNT(instance),
5992 reinterpret_cast<TpSlotRefcntTestObject*>(instance.get())
5993 ->initial_refcnt);
5994}
5995
5996} // namespace testing
5997} // namespace py