1 #include "parts.h"
2 #include <stddef.h> // offsetof()
3
4
5 static struct PyModuleDef *_testcapimodule = NULL; // set at initialization
6
7 /* Tests for heap types (PyType_From*) */
8
pytype_fromspec_meta(PyObject * self,PyObject * meta)9 static PyObject *pytype_fromspec_meta(PyObject* self, PyObject *meta)
10 {
11 if (!PyType_Check(meta)) {
12 PyErr_SetString(
13 PyExc_TypeError,
14 "pytype_fromspec_meta: must be invoked with a type argument!");
15 return NULL;
16 }
17
18 PyType_Slot HeapCTypeViaMetaclass_slots[] = {
19 {0},
20 };
21
22 PyType_Spec HeapCTypeViaMetaclass_spec = {
23 "_testcapi.HeapCTypeViaMetaclass",
24 sizeof(PyObject),
25 0,
26 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
27 HeapCTypeViaMetaclass_slots
28 };
29
30 return PyType_FromMetaclass(
31 (PyTypeObject *) meta, NULL, &HeapCTypeViaMetaclass_spec, NULL);
32 }
33
34
35 static PyType_Slot empty_type_slots[] = {
36 {0, 0},
37 };
38
39 static PyType_Spec MinimalMetaclass_spec = {
40 .name = "_testcapi.MinimalMetaclass",
41 .basicsize = sizeof(PyHeapTypeObject),
42 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
43 .slots = empty_type_slots,
44 };
45
46 static PyType_Spec MinimalType_spec = {
47 .name = "_testcapi.MinimalSpecType",
48 .basicsize = 0, // Updated later
49 .flags = Py_TPFLAGS_DEFAULT,
50 .slots = empty_type_slots,
51 };
52
53
54 static PyObject *
test_from_spec_metatype_inheritance(PyObject * self,PyObject * Py_UNUSED (ignored))55 test_from_spec_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
56 {
57 PyObject *metaclass = NULL;
58 PyObject *class = NULL;
59 PyObject *new = NULL;
60 PyObject *subclasses = NULL;
61 PyObject *result = NULL;
62 int r;
63
64 metaclass = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
65 if (metaclass == NULL) {
66 goto finally;
67 }
68 class = PyObject_CallFunction(metaclass, "s(){}", "TestClass");
69 if (class == NULL) {
70 goto finally;
71 }
72
73 MinimalType_spec.basicsize = (int)(((PyTypeObject*)class)->tp_basicsize);
74 new = PyType_FromSpecWithBases(&MinimalType_spec, class);
75 if (new == NULL) {
76 goto finally;
77 }
78 if (Py_TYPE(new) != (PyTypeObject*)metaclass) {
79 PyErr_SetString(PyExc_AssertionError,
80 "Metaclass not set properly!");
81 goto finally;
82 }
83
84 /* Assert that __subclasses__ is updated */
85 subclasses = PyObject_CallMethod(class, "__subclasses__", "");
86 if (!subclasses) {
87 goto finally;
88 }
89 r = PySequence_Contains(subclasses, new);
90 if (r < 0) {
91 goto finally;
92 }
93 if (r == 0) {
94 PyErr_SetString(PyExc_AssertionError,
95 "subclasses not set properly!");
96 goto finally;
97 }
98
99 result = Py_NewRef(Py_None);
100
101 finally:
102 Py_XDECREF(metaclass);
103 Py_XDECREF(class);
104 Py_XDECREF(new);
105 Py_XDECREF(subclasses);
106 return result;
107 }
108
109
110 static PyObject *
test_from_spec_invalid_metatype_inheritance(PyObject * self,PyObject * Py_UNUSED (ignored))111 test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
112 {
113 PyObject *metaclass_a = NULL;
114 PyObject *metaclass_b = NULL;
115 PyObject *class_a = NULL;
116 PyObject *class_b = NULL;
117 PyObject *bases = NULL;
118 PyObject *new = NULL;
119 PyObject *meta_error_string = NULL;
120 PyObject *exc = NULL;
121 PyObject *result = NULL;
122 PyObject *message = NULL;
123 PyObject *args = NULL;
124
125 metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
126 if (metaclass_a == NULL) {
127 goto finally;
128 }
129 metaclass_b = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
130 if (metaclass_b == NULL) {
131 goto finally;
132 }
133 class_a = PyObject_CallFunction(metaclass_a, "s(){}", "TestClassA");
134 if (class_a == NULL) {
135 goto finally;
136 }
137
138 class_b = PyObject_CallFunction(metaclass_b, "s(){}", "TestClassB");
139 if (class_b == NULL) {
140 goto finally;
141 }
142
143 bases = PyTuple_Pack(2, class_a, class_b);
144 if (bases == NULL) {
145 goto finally;
146 }
147
148 /*
149 * The following should raise a TypeError due to a MetaClass conflict.
150 */
151 new = PyType_FromSpecWithBases(&MinimalType_spec, bases);
152 if (new != NULL) {
153 PyErr_SetString(PyExc_AssertionError,
154 "MetaType conflict not recognized by PyType_FromSpecWithBases");
155 goto finally;
156 }
157
158 // Assert that the correct exception was raised
159 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
160 exc = PyErr_GetRaisedException();
161 args = PyException_GetArgs(exc);
162 if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) {
163 PyErr_SetString(PyExc_AssertionError,
164 "TypeError args are not a one-tuple");
165 goto finally;
166 }
167 message = Py_NewRef(PyTuple_GET_ITEM(args, 0));
168 meta_error_string = PyUnicode_FromString("metaclass conflict:");
169 if (meta_error_string == NULL) {
170 goto finally;
171 }
172 int res = PyUnicode_Contains(message, meta_error_string);
173 if (res < 0) {
174 goto finally;
175 }
176 if (res == 0) {
177 PyErr_SetString(PyExc_AssertionError,
178 "TypeError did not include expected message.");
179 goto finally;
180 }
181 result = Py_NewRef(Py_None);
182 }
183 finally:
184 Py_XDECREF(metaclass_a);
185 Py_XDECREF(metaclass_b);
186 Py_XDECREF(bases);
187 Py_XDECREF(new);
188 Py_XDECREF(meta_error_string);
189 Py_XDECREF(exc);
190 Py_XDECREF(message);
191 Py_XDECREF(class_a);
192 Py_XDECREF(class_b);
193 Py_XDECREF(args);
194 return result;
195 }
196
197
198 static PyObject *
simple_str(PyObject * self)199 simple_str(PyObject *self) {
200 return PyUnicode_FromString("<test>");
201 }
202
203
204 static PyObject *
test_type_from_ephemeral_spec(PyObject * self,PyObject * Py_UNUSED (ignored))205 test_type_from_ephemeral_spec(PyObject *self, PyObject *Py_UNUSED(ignored))
206 {
207 // Test that a heap type can be created from a spec that's later deleted
208 // (along with all its contents).
209 // All necessary data must be copied and held by the class
210 PyType_Spec *spec = NULL;
211 char *name = NULL;
212 char *doc = NULL;
213 PyType_Slot *slots = NULL;
214 PyObject *class = NULL;
215 PyObject *instance = NULL;
216 PyObject *obj = NULL;
217 PyObject *result = NULL;
218
219 /* create a spec (and all its contents) on the heap */
220
221 const char NAME[] = "testcapi._Test";
222 const char DOC[] = "a test class";
223
224 spec = PyMem_New(PyType_Spec, 1);
225 if (spec == NULL) {
226 PyErr_NoMemory();
227 goto finally;
228 }
229 name = PyMem_New(char, sizeof(NAME));
230 if (name == NULL) {
231 PyErr_NoMemory();
232 goto finally;
233 }
234 memcpy(name, NAME, sizeof(NAME));
235
236 doc = PyMem_New(char, sizeof(DOC));
237 if (doc == NULL) {
238 PyErr_NoMemory();
239 goto finally;
240 }
241 memcpy(doc, DOC, sizeof(DOC));
242
243 spec->name = name;
244 spec->basicsize = sizeof(PyObject);
245 spec->itemsize = 0;
246 spec->flags = Py_TPFLAGS_DEFAULT;
247 slots = PyMem_New(PyType_Slot, 3);
248 if (slots == NULL) {
249 PyErr_NoMemory();
250 goto finally;
251 }
252 slots[0].slot = Py_tp_str;
253 slots[0].pfunc = simple_str;
254 slots[1].slot = Py_tp_doc;
255 slots[1].pfunc = doc;
256 slots[2].slot = 0;
257 slots[2].pfunc = NULL;
258 spec->slots = slots;
259
260 /* create the class */
261
262 class = PyType_FromSpec(spec);
263 if (class == NULL) {
264 goto finally;
265 }
266
267 /* deallocate the spec (and all contents) */
268
269 // (Explicitly overwrite memory before freeing,
270 // so bugs show themselves even without the debug allocator's help.)
271 memset(spec, 0xdd, sizeof(PyType_Spec));
272 PyMem_Del(spec);
273 spec = NULL;
274 memset(name, 0xdd, sizeof(NAME));
275 PyMem_Del(name);
276 name = NULL;
277 memset(doc, 0xdd, sizeof(DOC));
278 PyMem_Del(doc);
279 doc = NULL;
280 memset(slots, 0xdd, 3 * sizeof(PyType_Slot));
281 PyMem_Del(slots);
282 slots = NULL;
283
284 /* check that everything works */
285
286 PyTypeObject *class_tp = (PyTypeObject *)class;
287 PyHeapTypeObject *class_ht = (PyHeapTypeObject *)class;
288 assert(strcmp(class_tp->tp_name, "testcapi._Test") == 0);
289 assert(strcmp(PyUnicode_AsUTF8(class_ht->ht_name), "_Test") == 0);
290 assert(strcmp(PyUnicode_AsUTF8(class_ht->ht_qualname), "_Test") == 0);
291 assert(strcmp(class_tp->tp_doc, "a test class") == 0);
292
293 // call and check __str__
294 instance = PyObject_CallNoArgs(class);
295 if (instance == NULL) {
296 goto finally;
297 }
298 obj = PyObject_Str(instance);
299 if (obj == NULL) {
300 goto finally;
301 }
302 assert(strcmp(PyUnicode_AsUTF8(obj), "<test>") == 0);
303 Py_CLEAR(obj);
304
305 result = Py_NewRef(Py_None);
306 finally:
307 PyMem_Del(spec);
308 PyMem_Del(name);
309 PyMem_Del(doc);
310 PyMem_Del(slots);
311 Py_XDECREF(class);
312 Py_XDECREF(instance);
313 Py_XDECREF(obj);
314 return result;
315 }
316
317 PyType_Slot repeated_doc_slots[] = {
318 {Py_tp_doc, "A class used for tests·"},
319 {Py_tp_doc, "A class used for tests"},
320 {0, 0},
321 };
322
323 PyType_Spec repeated_doc_slots_spec = {
324 .name = "RepeatedDocSlotClass",
325 .basicsize = sizeof(PyObject),
326 .slots = repeated_doc_slots,
327 };
328
329 typedef struct {
330 PyObject_HEAD
331 int data;
332 } HeapCTypeWithDataObject;
333
334
335 static struct PyMemberDef members_to_repeat[] = {
336 {"Py_T_INT", Py_T_INT, offsetof(HeapCTypeWithDataObject, data), 0, NULL},
337 {NULL}
338 };
339
340 PyType_Slot repeated_members_slots[] = {
341 {Py_tp_members, members_to_repeat},
342 {Py_tp_members, members_to_repeat},
343 {0, 0},
344 };
345
346 PyType_Spec repeated_members_slots_spec = {
347 .name = "RepeatedMembersSlotClass",
348 .basicsize = sizeof(HeapCTypeWithDataObject),
349 .slots = repeated_members_slots,
350 };
351
352 static PyObject *
create_type_from_repeated_slots(PyObject * self,PyObject * variant_obj)353 create_type_from_repeated_slots(PyObject *self, PyObject *variant_obj)
354 {
355 PyObject *class = NULL;
356 int variant = PyLong_AsLong(variant_obj);
357 if (PyErr_Occurred()) {
358 return NULL;
359 }
360 switch (variant) {
361 case 0:
362 class = PyType_FromSpec(&repeated_doc_slots_spec);
363 break;
364 case 1:
365 class = PyType_FromSpec(&repeated_members_slots_spec);
366 break;
367 default:
368 PyErr_SetString(PyExc_ValueError, "bad test variant");
369 break;
370 }
371 return class;
372 }
373
374
375 static PyObject *
make_immutable_type_with_base(PyObject * self,PyObject * base)376 make_immutable_type_with_base(PyObject *self, PyObject *base)
377 {
378 assert(PyType_Check(base));
379 PyType_Spec ImmutableSubclass_spec = {
380 .name = "ImmutableSubclass",
381 .basicsize = (int)((PyTypeObject*)base)->tp_basicsize,
382 .slots = empty_type_slots,
383 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE,
384 };
385 return PyType_FromSpecWithBases(&ImmutableSubclass_spec, base);
386 }
387
388 static PyObject *
make_type_with_base(PyObject * self,PyObject * base)389 make_type_with_base(PyObject *self, PyObject *base)
390 {
391 assert(PyType_Check(base));
392 PyType_Spec ImmutableSubclass_spec = {
393 .name = "_testcapi.Subclass",
394 .basicsize = (int)((PyTypeObject*)base)->tp_basicsize,
395 .slots = empty_type_slots,
396 .flags = Py_TPFLAGS_DEFAULT,
397 };
398 return PyType_FromSpecWithBases(&ImmutableSubclass_spec, base);
399 }
400
401
402 static PyObject *
pyobject_getitemdata(PyObject * self,PyObject * o)403 pyobject_getitemdata(PyObject *self, PyObject *o)
404 {
405 void *pointer = PyObject_GetItemData(o);
406 if (pointer == NULL) {
407 return NULL;
408 }
409 return PyLong_FromVoidPtr(pointer);
410 }
411
412
413 static PyMethodDef TestMethods[] = {
414 {"pytype_fromspec_meta", pytype_fromspec_meta, METH_O},
415 {"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
416 {"create_type_from_repeated_slots",
417 create_type_from_repeated_slots, METH_O},
418 {"test_from_spec_metatype_inheritance", test_from_spec_metatype_inheritance,
419 METH_NOARGS},
420 {"test_from_spec_invalid_metatype_inheritance",
421 test_from_spec_invalid_metatype_inheritance,
422 METH_NOARGS},
423 {"make_immutable_type_with_base", make_immutable_type_with_base, METH_O},
424 {"make_type_with_base", make_type_with_base, METH_O},
425 {"pyobject_getitemdata", pyobject_getitemdata, METH_O},
426 {NULL},
427 };
428
429
430 PyDoc_STRVAR(heapdocctype__doc__,
431 "HeapDocCType(arg1, arg2)\n"
432 "--\n"
433 "\n"
434 "somedoc");
435
436 typedef struct {
437 PyObject_HEAD
438 } HeapDocCTypeObject;
439
440 static PyType_Slot HeapDocCType_slots[] = {
441 {Py_tp_doc, (char*)heapdocctype__doc__},
442 {0},
443 };
444
445 static PyType_Spec HeapDocCType_spec = {
446 "_testcapi.HeapDocCType",
447 sizeof(HeapDocCTypeObject),
448 0,
449 Py_TPFLAGS_DEFAULT,
450 HeapDocCType_slots
451 };
452
453 typedef struct {
454 PyObject_HEAD
455 } NullTpDocTypeObject;
456
457 static PyType_Slot NullTpDocType_slots[] = {
458 {Py_tp_doc, NULL},
459 {0, 0},
460 };
461
462 static PyType_Spec NullTpDocType_spec = {
463 "_testcapi.NullTpDocType",
464 sizeof(NullTpDocTypeObject),
465 0,
466 Py_TPFLAGS_DEFAULT,
467 NullTpDocType_slots
468 };
469
470
471 PyDoc_STRVAR(heapgctype__doc__,
472 "A heap type with GC, and with overridden dealloc.\n\n"
473 "The 'value' attribute is set to 10 in __init__.");
474
475 typedef struct {
476 PyObject_HEAD
477 int value;
478 } HeapCTypeObject;
479
480 static struct PyMemberDef heapctype_members[] = {
481 {"value", Py_T_INT, offsetof(HeapCTypeObject, value)},
482 {NULL} /* Sentinel */
483 };
484
485 static int
heapctype_init(PyObject * self,PyObject * args,PyObject * kwargs)486 heapctype_init(PyObject *self, PyObject *args, PyObject *kwargs)
487 {
488 ((HeapCTypeObject *)self)->value = 10;
489 return 0;
490 }
491
492 static int
heapgcctype_traverse(HeapCTypeObject * self,visitproc visit,void * arg)493 heapgcctype_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
494 {
495 Py_VISIT(Py_TYPE(self));
496 return 0;
497 }
498
499 static void
heapgcctype_dealloc(HeapCTypeObject * self)500 heapgcctype_dealloc(HeapCTypeObject *self)
501 {
502 PyTypeObject *tp = Py_TYPE(self);
503 PyObject_GC_UnTrack(self);
504 PyObject_GC_Del(self);
505 Py_DECREF(tp);
506 }
507
508 static PyType_Slot HeapGcCType_slots[] = {
509 {Py_tp_init, heapctype_init},
510 {Py_tp_members, heapctype_members},
511 {Py_tp_dealloc, heapgcctype_dealloc},
512 {Py_tp_traverse, heapgcctype_traverse},
513 {Py_tp_doc, (char*)heapgctype__doc__},
514 {0, 0},
515 };
516
517 static PyType_Spec HeapGcCType_spec = {
518 "_testcapi.HeapGcCType",
519 sizeof(HeapCTypeObject),
520 0,
521 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
522 HeapGcCType_slots
523 };
524
525 PyDoc_STRVAR(heapctype__doc__,
526 "A heap type without GC, but with overridden dealloc.\n\n"
527 "The 'value' attribute is set to 10 in __init__.");
528
529 static void
heapctype_dealloc(HeapCTypeObject * self)530 heapctype_dealloc(HeapCTypeObject *self)
531 {
532 PyTypeObject *tp = Py_TYPE(self);
533 PyObject_Free(self);
534 Py_DECREF(tp);
535 }
536
537 static PyType_Slot HeapCType_slots[] = {
538 {Py_tp_init, heapctype_init},
539 {Py_tp_members, heapctype_members},
540 {Py_tp_dealloc, heapctype_dealloc},
541 {Py_tp_doc, (char*)heapctype__doc__},
542 {0, 0},
543 };
544
545 static PyType_Spec HeapCType_spec = {
546 "_testcapi.HeapCType",
547 sizeof(HeapCTypeObject),
548 0,
549 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
550 HeapCType_slots
551 };
552
553 PyDoc_STRVAR(heapctypesubclass__doc__,
554 "Subclass of HeapCType, without GC.\n\n"
555 "__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
556
557 typedef struct {
558 HeapCTypeObject base;
559 int value2;
560 } HeapCTypeSubclassObject;
561
562 static int
heapctypesubclass_init(PyObject * self,PyObject * args,PyObject * kwargs)563 heapctypesubclass_init(PyObject *self, PyObject *args, PyObject *kwargs)
564 {
565 /* Call __init__ of the superclass */
566 if (heapctype_init(self, args, kwargs) < 0) {
567 return -1;
568 }
569 /* Initialize additional element */
570 ((HeapCTypeSubclassObject *)self)->value2 = 20;
571 return 0;
572 }
573
574 static struct PyMemberDef heapctypesubclass_members[] = {
575 {"value2", Py_T_INT, offsetof(HeapCTypeSubclassObject, value2)},
576 {NULL} /* Sentinel */
577 };
578
579 static PyType_Slot HeapCTypeSubclass_slots[] = {
580 {Py_tp_init, heapctypesubclass_init},
581 {Py_tp_members, heapctypesubclass_members},
582 {Py_tp_doc, (char*)heapctypesubclass__doc__},
583 {0, 0},
584 };
585
586 static PyType_Spec HeapCTypeSubclass_spec = {
587 "_testcapi.HeapCTypeSubclass",
588 sizeof(HeapCTypeSubclassObject),
589 0,
590 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
591 HeapCTypeSubclass_slots
592 };
593
594 PyDoc_STRVAR(heapctypewithbuffer__doc__,
595 "Heap type with buffer support.\n\n"
596 "The buffer is set to [b'1', b'2', b'3', b'4']");
597
598 typedef struct {
599 HeapCTypeObject base;
600 char buffer[4];
601 } HeapCTypeWithBufferObject;
602
603 static int
heapctypewithbuffer_getbuffer(HeapCTypeWithBufferObject * self,Py_buffer * view,int flags)604 heapctypewithbuffer_getbuffer(HeapCTypeWithBufferObject *self, Py_buffer *view, int flags)
605 {
606 self->buffer[0] = '1';
607 self->buffer[1] = '2';
608 self->buffer[2] = '3';
609 self->buffer[3] = '4';
610 return PyBuffer_FillInfo(
611 view, (PyObject*)self, (void *)self->buffer, 4, 1, flags);
612 }
613
614 static void
heapctypewithbuffer_releasebuffer(HeapCTypeWithBufferObject * self,Py_buffer * view)615 heapctypewithbuffer_releasebuffer(HeapCTypeWithBufferObject *self, Py_buffer *view)
616 {
617 assert(view->obj == (void*) self);
618 }
619
620 static PyType_Slot HeapCTypeWithBuffer_slots[] = {
621 {Py_bf_getbuffer, heapctypewithbuffer_getbuffer},
622 {Py_bf_releasebuffer, heapctypewithbuffer_releasebuffer},
623 {Py_tp_doc, (char*)heapctypewithbuffer__doc__},
624 {0, 0},
625 };
626
627 static PyType_Spec HeapCTypeWithBuffer_spec = {
628 "_testcapi.HeapCTypeWithBuffer",
629 sizeof(HeapCTypeWithBufferObject),
630 0,
631 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
632 HeapCTypeWithBuffer_slots
633 };
634
635 PyDoc_STRVAR(heapctypesubclasswithfinalizer__doc__,
636 "Subclass of HeapCType with a finalizer that reassigns __class__.\n\n"
637 "__class__ is set to plain HeapCTypeSubclass during finalization.\n"
638 "__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
639
640 static int
heapctypesubclasswithfinalizer_init(PyObject * self,PyObject * args,PyObject * kwargs)641 heapctypesubclasswithfinalizer_init(PyObject *self, PyObject *args, PyObject *kwargs)
642 {
643 PyTypeObject *base = (PyTypeObject *)PyType_GetSlot(Py_TYPE(self), Py_tp_base);
644 initproc base_init = PyType_GetSlot(base, Py_tp_init);
645 base_init(self, args, kwargs);
646 return 0;
647 }
648
649 static void
heapctypesubclasswithfinalizer_finalize(PyObject * self)650 heapctypesubclasswithfinalizer_finalize(PyObject *self)
651 {
652 PyObject *oldtype = NULL, *newtype = NULL, *refcnt = NULL;
653
654 /* Save the current exception, if any. */
655 PyObject *exc = PyErr_GetRaisedException();
656
657 if (_testcapimodule == NULL) {
658 goto cleanup_finalize;
659 }
660 PyObject *m = PyState_FindModule(_testcapimodule);
661 if (m == NULL) {
662 goto cleanup_finalize;
663 }
664 oldtype = PyObject_GetAttrString(m, "HeapCTypeSubclassWithFinalizer");
665 if (oldtype == NULL) {
666 goto cleanup_finalize;
667 }
668 newtype = PyObject_GetAttrString(m, "HeapCTypeSubclass");
669 if (newtype == NULL) {
670 goto cleanup_finalize;
671 }
672
673 if (PyObject_SetAttrString(self, "__class__", newtype) < 0) {
674 goto cleanup_finalize;
675 }
676 refcnt = PyLong_FromSsize_t(Py_REFCNT(oldtype));
677 if (refcnt == NULL) {
678 goto cleanup_finalize;
679 }
680 if (PyObject_SetAttrString(oldtype, "refcnt_in_del", refcnt) < 0) {
681 goto cleanup_finalize;
682 }
683 Py_DECREF(refcnt);
684 refcnt = PyLong_FromSsize_t(Py_REFCNT(newtype));
685 if (refcnt == NULL) {
686 goto cleanup_finalize;
687 }
688 if (PyObject_SetAttrString(newtype, "refcnt_in_del", refcnt) < 0) {
689 goto cleanup_finalize;
690 }
691
692 cleanup_finalize:
693 Py_XDECREF(oldtype);
694 Py_XDECREF(newtype);
695 Py_XDECREF(refcnt);
696
697 /* Restore the saved exception. */
698 PyErr_SetRaisedException(exc);
699 }
700
701 static PyType_Slot HeapCTypeSubclassWithFinalizer_slots[] = {
702 {Py_tp_init, heapctypesubclasswithfinalizer_init},
703 {Py_tp_members, heapctypesubclass_members},
704 {Py_tp_finalize, heapctypesubclasswithfinalizer_finalize},
705 {Py_tp_doc, (char*)heapctypesubclasswithfinalizer__doc__},
706 {0, 0},
707 };
708
709 static PyType_Spec HeapCTypeSubclassWithFinalizer_spec = {
710 "_testcapi.HeapCTypeSubclassWithFinalizer",
711 sizeof(HeapCTypeSubclassObject),
712 0,
713 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE,
714 HeapCTypeSubclassWithFinalizer_slots
715 };
716
717 static PyType_Slot HeapCTypeMetaclass_slots[] = {
718 {0},
719 };
720
721 static PyType_Spec HeapCTypeMetaclass_spec = {
722 "_testcapi.HeapCTypeMetaclass",
723 sizeof(PyHeapTypeObject),
724 sizeof(PyMemberDef),
725 Py_TPFLAGS_DEFAULT,
726 HeapCTypeMetaclass_slots
727 };
728
729 static PyObject *
heap_ctype_metaclass_custom_tp_new(PyTypeObject * tp,PyObject * args,PyObject * kwargs)730 heap_ctype_metaclass_custom_tp_new(PyTypeObject *tp, PyObject *args, PyObject *kwargs)
731 {
732 return PyType_Type.tp_new(tp, args, kwargs);
733 }
734
735 static PyType_Slot HeapCTypeMetaclassCustomNew_slots[] = {
736 { Py_tp_new, heap_ctype_metaclass_custom_tp_new },
737 {0},
738 };
739
740 static PyType_Spec HeapCTypeMetaclassCustomNew_spec = {
741 "_testcapi.HeapCTypeMetaclassCustomNew",
742 sizeof(PyHeapTypeObject),
743 sizeof(PyMemberDef),
744 Py_TPFLAGS_DEFAULT,
745 HeapCTypeMetaclassCustomNew_slots
746 };
747
748 static PyType_Spec HeapCTypeMetaclassNullNew_spec = {
749 .name = "_testcapi.HeapCTypeMetaclassNullNew",
750 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
751 .slots = empty_type_slots
752 };
753
754
755 typedef struct {
756 PyObject_HEAD
757 PyObject *dict;
758 } HeapCTypeWithDictObject;
759
760 static void
heapctypewithdict_dealloc(HeapCTypeWithDictObject * self)761 heapctypewithdict_dealloc(HeapCTypeWithDictObject* self)
762 {
763
764 PyTypeObject *tp = Py_TYPE(self);
765 Py_XDECREF(self->dict);
766 PyObject_Free(self);
767 Py_DECREF(tp);
768 }
769
770 static PyGetSetDef heapctypewithdict_getsetlist[] = {
771 {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
772 {NULL} /* Sentinel */
773 };
774
775 static struct PyMemberDef heapctypewithdict_members[] = {
776 {"dictobj", _Py_T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)},
777 {"__dictoffset__", Py_T_PYSSIZET, offsetof(HeapCTypeWithDictObject, dict), Py_READONLY},
778 {NULL} /* Sentinel */
779 };
780
781 static PyType_Slot HeapCTypeWithDict_slots[] = {
782 {Py_tp_members, heapctypewithdict_members},
783 {Py_tp_getset, heapctypewithdict_getsetlist},
784 {Py_tp_dealloc, heapctypewithdict_dealloc},
785 {0, 0},
786 };
787
788 static PyType_Spec HeapCTypeWithDict_spec = {
789 "_testcapi.HeapCTypeWithDict",
790 sizeof(HeapCTypeWithDictObject),
791 0,
792 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
793 HeapCTypeWithDict_slots
794 };
795
796 static PyType_Spec HeapCTypeWithDict2_spec = {
797 "_testcapi.HeapCTypeWithDict2",
798 sizeof(HeapCTypeWithDictObject),
799 0,
800 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
801 HeapCTypeWithDict_slots
802 };
803
804 static int
heapmanaged_traverse(HeapCTypeObject * self,visitproc visit,void * arg)805 heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
806 {
807 Py_VISIT(Py_TYPE(self));
808 return PyObject_VisitManagedDict((PyObject *)self, visit, arg);
809 }
810
811 static int
heapmanaged_clear(HeapCTypeObject * self)812 heapmanaged_clear(HeapCTypeObject *self)
813 {
814 PyObject_ClearManagedDict((PyObject *)self);
815 return 0;
816 }
817
818 static void
heapmanaged_dealloc(HeapCTypeObject * self)819 heapmanaged_dealloc(HeapCTypeObject *self)
820 {
821 PyTypeObject *tp = Py_TYPE(self);
822 PyObject_ClearManagedDict((PyObject *)self);
823 PyObject_GC_UnTrack(self);
824 PyObject_GC_Del(self);
825 Py_DECREF(tp);
826 }
827
828 static PyType_Slot HeapCTypeWithManagedDict_slots[] = {
829 {Py_tp_traverse, heapmanaged_traverse},
830 {Py_tp_getset, heapctypewithdict_getsetlist},
831 {Py_tp_clear, heapmanaged_clear},
832 {Py_tp_dealloc, heapmanaged_dealloc},
833 {0, 0},
834 };
835
836 static PyType_Spec HeapCTypeWithManagedDict_spec = {
837 "_testcapi.HeapCTypeWithManagedDict",
838 sizeof(PyObject),
839 0,
840 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT,
841 HeapCTypeWithManagedDict_slots
842 };
843
844 static void
heapctypewithmanagedweakref_dealloc(PyObject * self)845 heapctypewithmanagedweakref_dealloc(PyObject* self)
846 {
847
848 PyTypeObject *tp = Py_TYPE(self);
849 PyObject_ClearWeakRefs(self);
850 PyObject_GC_UnTrack(self);
851 PyObject_GC_Del(self);
852 Py_DECREF(tp);
853 }
854
855 static PyType_Slot HeapCTypeWithManagedWeakref_slots[] = {
856 {Py_tp_traverse, heapgcctype_traverse},
857 {Py_tp_getset, heapctypewithdict_getsetlist},
858 {Py_tp_dealloc, heapctypewithmanagedweakref_dealloc},
859 {0, 0},
860 };
861
862 static PyType_Spec HeapCTypeWithManagedWeakref_spec = {
863 "_testcapi.HeapCTypeWithManagedWeakref",
864 sizeof(PyObject),
865 0,
866 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_WEAKREF,
867 HeapCTypeWithManagedWeakref_slots
868 };
869
870 static struct PyMemberDef heapctypewithnegativedict_members[] = {
871 {"dictobj", _Py_T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)},
872 {"__dictoffset__", Py_T_PYSSIZET, -(Py_ssize_t)sizeof(void*), Py_READONLY},
873 {NULL} /* Sentinel */
874 };
875
876 static PyType_Slot HeapCTypeWithNegativeDict_slots[] = {
877 {Py_tp_members, heapctypewithnegativedict_members},
878 {Py_tp_getset, heapctypewithdict_getsetlist},
879 {Py_tp_dealloc, heapctypewithdict_dealloc},
880 {0, 0},
881 };
882
883 static PyType_Spec HeapCTypeWithNegativeDict_spec = {
884 "_testcapi.HeapCTypeWithNegativeDict",
885 sizeof(HeapCTypeWithDictObject),
886 0,
887 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
888 HeapCTypeWithNegativeDict_slots
889 };
890
891 typedef struct {
892 PyObject_HEAD
893 PyObject *weakreflist;
894 } HeapCTypeWithWeakrefObject;
895
896 static struct PyMemberDef heapctypewithweakref_members[] = {
897 {"weakreflist", _Py_T_OBJECT, offsetof(HeapCTypeWithWeakrefObject, weakreflist)},
898 {"__weaklistoffset__", Py_T_PYSSIZET,
899 offsetof(HeapCTypeWithWeakrefObject, weakreflist), Py_READONLY},
900 {NULL} /* Sentinel */
901 };
902
903 static void
heapctypewithweakref_dealloc(HeapCTypeWithWeakrefObject * self)904 heapctypewithweakref_dealloc(HeapCTypeWithWeakrefObject* self)
905 {
906
907 PyTypeObject *tp = Py_TYPE(self);
908 if (self->weakreflist != NULL)
909 PyObject_ClearWeakRefs((PyObject *) self);
910 Py_XDECREF(self->weakreflist);
911 PyObject_Free(self);
912 Py_DECREF(tp);
913 }
914
915 static PyType_Slot HeapCTypeWithWeakref_slots[] = {
916 {Py_tp_members, heapctypewithweakref_members},
917 {Py_tp_dealloc, heapctypewithweakref_dealloc},
918 {0, 0},
919 };
920
921 static PyType_Spec HeapCTypeWithWeakref_spec = {
922 "_testcapi.HeapCTypeWithWeakref",
923 sizeof(HeapCTypeWithWeakrefObject),
924 0,
925 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
926 HeapCTypeWithWeakref_slots
927 };
928
929 static PyType_Spec HeapCTypeWithWeakref2_spec = {
930 "_testcapi.HeapCTypeWithWeakref2",
931 sizeof(HeapCTypeWithWeakrefObject),
932 0,
933 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
934 HeapCTypeWithWeakref_slots
935 };
936
937 PyDoc_STRVAR(heapctypesetattr__doc__,
938 "A heap type without GC, but with overridden __setattr__.\n\n"
939 "The 'value' attribute is set to 10 in __init__ and updated via attribute setting.");
940
941 typedef struct {
942 PyObject_HEAD
943 long value;
944 } HeapCTypeSetattrObject;
945
946 static struct PyMemberDef heapctypesetattr_members[] = {
947 {"pvalue", Py_T_LONG, offsetof(HeapCTypeSetattrObject, value)},
948 {NULL} /* Sentinel */
949 };
950
951 static int
heapctypesetattr_init(PyObject * self,PyObject * args,PyObject * kwargs)952 heapctypesetattr_init(PyObject *self, PyObject *args, PyObject *kwargs)
953 {
954 ((HeapCTypeSetattrObject *)self)->value = 10;
955 return 0;
956 }
957
958 static void
heapctypesetattr_dealloc(HeapCTypeSetattrObject * self)959 heapctypesetattr_dealloc(HeapCTypeSetattrObject *self)
960 {
961 PyTypeObject *tp = Py_TYPE(self);
962 PyObject_Free(self);
963 Py_DECREF(tp);
964 }
965
966 static int
heapctypesetattr_setattro(HeapCTypeSetattrObject * self,PyObject * attr,PyObject * value)967 heapctypesetattr_setattro(HeapCTypeSetattrObject *self, PyObject *attr, PyObject *value)
968 {
969 PyObject *svalue = PyUnicode_FromString("value");
970 if (svalue == NULL)
971 return -1;
972 int eq = PyObject_RichCompareBool(svalue, attr, Py_EQ);
973 Py_DECREF(svalue);
974 if (eq < 0)
975 return -1;
976 if (!eq) {
977 return PyObject_GenericSetAttr((PyObject*) self, attr, value);
978 }
979 if (value == NULL) {
980 self->value = 0;
981 return 0;
982 }
983 PyObject *ivalue = PyNumber_Long(value);
984 if (ivalue == NULL)
985 return -1;
986 long v = PyLong_AsLong(ivalue);
987 Py_DECREF(ivalue);
988 if (v == -1 && PyErr_Occurred())
989 return -1;
990 self->value = v;
991 return 0;
992 }
993
994 static PyType_Slot HeapCTypeSetattr_slots[] = {
995 {Py_tp_init, heapctypesetattr_init},
996 {Py_tp_members, heapctypesetattr_members},
997 {Py_tp_setattro, heapctypesetattr_setattro},
998 {Py_tp_dealloc, heapctypesetattr_dealloc},
999 {Py_tp_doc, (char*)heapctypesetattr__doc__},
1000 {0, 0},
1001 };
1002
1003 static PyType_Spec HeapCTypeSetattr_spec = {
1004 "_testcapi.HeapCTypeSetattr",
1005 sizeof(HeapCTypeSetattrObject),
1006 0,
1007 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1008 HeapCTypeSetattr_slots
1009 };
1010
1011 PyDoc_STRVAR(HeapCCollection_doc,
1012 "Tuple-like heap type that uses PyObject_GetItemData for items.");
1013
1014 static PyObject*
HeapCCollection_new(PyTypeObject * subtype,PyObject * args,PyObject * kwds)1015 HeapCCollection_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
1016 {
1017 PyObject *self = NULL;
1018 PyObject *result = NULL;
1019
1020 Py_ssize_t size = PyTuple_GET_SIZE(args);
1021 self = subtype->tp_alloc(subtype, size);
1022 if (!self) {
1023 goto finally;
1024 }
1025 PyObject **data = PyObject_GetItemData(self);
1026 if (!data) {
1027 goto finally;
1028 }
1029
1030 for (Py_ssize_t i = 0; i < size; i++) {
1031 data[i] = Py_NewRef(PyTuple_GET_ITEM(args, i));
1032 }
1033
1034 result = self;
1035 self = NULL;
1036 finally:
1037 Py_XDECREF(self);
1038 return result;
1039 }
1040
1041 static Py_ssize_t
HeapCCollection_length(PyVarObject * self)1042 HeapCCollection_length(PyVarObject *self)
1043 {
1044 return Py_SIZE(self);
1045 }
1046
1047 static PyObject*
HeapCCollection_item(PyObject * self,Py_ssize_t i)1048 HeapCCollection_item(PyObject *self, Py_ssize_t i)
1049 {
1050 if (i < 0 || i >= Py_SIZE(self)) {
1051 return PyErr_Format(PyExc_IndexError, "index %zd out of range", i);
1052 }
1053 PyObject **data = PyObject_GetItemData(self);
1054 if (!data) {
1055 return NULL;
1056 }
1057 return Py_NewRef(data[i]);
1058 }
1059
1060 static int
HeapCCollection_traverse(PyObject * self,visitproc visit,void * arg)1061 HeapCCollection_traverse(PyObject *self, visitproc visit, void *arg)
1062 {
1063 PyObject **data = PyObject_GetItemData(self);
1064 if (!data) {
1065 return -1;
1066 }
1067 for (Py_ssize_t i = 0; i < Py_SIZE(self); i++) {
1068 Py_VISIT(data[i]);
1069 }
1070 return 0;
1071 }
1072
1073 static int
HeapCCollection_clear(PyObject * self)1074 HeapCCollection_clear(PyObject *self)
1075 {
1076 PyObject **data = PyObject_GetItemData(self);
1077 if (!data) {
1078 return -1;
1079 }
1080 Py_ssize_t size = Py_SIZE(self);
1081 Py_SET_SIZE(self, 0);
1082 for (Py_ssize_t i = 0; i < size; i++) {
1083 Py_CLEAR(data[i]);
1084 }
1085 return 0;
1086 }
1087
1088 static void
HeapCCollection_dealloc(PyObject * self)1089 HeapCCollection_dealloc(PyObject *self)
1090 {
1091 PyTypeObject *tp = Py_TYPE(self);
1092 HeapCCollection_clear(self);
1093 PyObject_GC_UnTrack(self);
1094 tp->tp_free(self);
1095 Py_DECREF(tp);
1096 }
1097
1098 static PyType_Slot HeapCCollection_slots[] = {
1099 {Py_tp_new, HeapCCollection_new},
1100 {Py_sq_length, HeapCCollection_length},
1101 {Py_sq_item, HeapCCollection_item},
1102 {Py_tp_traverse, HeapCCollection_traverse},
1103 {Py_tp_clear, HeapCCollection_clear},
1104 {Py_tp_dealloc, HeapCCollection_dealloc},
1105 {Py_tp_doc, (void *)HeapCCollection_doc},
1106 {0, 0},
1107 };
1108
1109 static PyType_Spec HeapCCollection_spec = {
1110 .name = "_testcapi.HeapCCollection",
1111 .basicsize = sizeof(PyVarObject),
1112 .itemsize = sizeof(PyObject*),
1113 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
1114 Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_ITEMS_AT_END),
1115 .slots = HeapCCollection_slots,
1116 };
1117
1118 int
_PyTestCapi_Init_Heaptype(PyObject * m)1119 _PyTestCapi_Init_Heaptype(PyObject *m) {
1120 _testcapimodule = PyModule_GetDef(m);
1121
1122 if (PyModule_AddFunctions(m, TestMethods) < 0) {
1123 return -1;
1124 }
1125
1126 #define ADD(name, value) do { \
1127 if (PyModule_Add(m, name, value) < 0) { \
1128 return -1; \
1129 } \
1130 } while (0)
1131
1132 PyObject *HeapDocCType = PyType_FromSpec(&HeapDocCType_spec);
1133 ADD("HeapDocCType", HeapDocCType);
1134
1135 /* bpo-41832: Add a new type to test PyType_FromSpec()
1136 now can accept a NULL tp_doc slot. */
1137 PyObject *NullTpDocType = PyType_FromSpec(&NullTpDocType_spec);
1138 ADD("NullTpDocType", NullTpDocType);
1139
1140 PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec);
1141 ADD("HeapGcCType", HeapGcCType);
1142
1143 PyObject *HeapCType = PyType_FromSpec(&HeapCType_spec);
1144 if (HeapCType == NULL) {
1145 return -1;
1146 }
1147 PyObject *subclass_bases = PyTuple_Pack(1, HeapCType);
1148 Py_DECREF(HeapCType);
1149 if (subclass_bases == NULL) {
1150 return -1;
1151 }
1152 PyObject *HeapCTypeSubclass = PyType_FromSpecWithBases(&HeapCTypeSubclass_spec, subclass_bases);
1153 Py_DECREF(subclass_bases);
1154 ADD("HeapCTypeSubclass", HeapCTypeSubclass);
1155
1156 PyObject *HeapCTypeWithDict = PyType_FromSpec(&HeapCTypeWithDict_spec);
1157 ADD("HeapCTypeWithDict", HeapCTypeWithDict);
1158
1159 PyObject *HeapCTypeWithDict2 = PyType_FromSpec(&HeapCTypeWithDict2_spec);
1160 ADD("HeapCTypeWithDict2", HeapCTypeWithDict2);
1161
1162 PyObject *HeapCTypeWithNegativeDict = PyType_FromSpec(&HeapCTypeWithNegativeDict_spec);
1163 ADD("HeapCTypeWithNegativeDict", HeapCTypeWithNegativeDict);
1164
1165 PyObject *HeapCTypeWithManagedDict = PyType_FromSpec(&HeapCTypeWithManagedDict_spec);
1166 ADD("HeapCTypeWithManagedDict", HeapCTypeWithManagedDict);
1167
1168 PyObject *HeapCTypeWithManagedWeakref = PyType_FromSpec(&HeapCTypeWithManagedWeakref_spec);
1169 ADD("HeapCTypeWithManagedWeakref", HeapCTypeWithManagedWeakref);
1170
1171 PyObject *HeapCTypeWithWeakref = PyType_FromSpec(&HeapCTypeWithWeakref_spec);
1172 ADD("HeapCTypeWithWeakref", HeapCTypeWithWeakref);
1173
1174 PyObject *HeapCTypeWithWeakref2 = PyType_FromSpec(&HeapCTypeWithWeakref2_spec);
1175 ADD("HeapCTypeWithWeakref2", HeapCTypeWithWeakref2);
1176
1177 PyObject *HeapCTypeWithBuffer = PyType_FromSpec(&HeapCTypeWithBuffer_spec);
1178 ADD("HeapCTypeWithBuffer", HeapCTypeWithBuffer);
1179
1180 PyObject *HeapCTypeSetattr = PyType_FromSpec(&HeapCTypeSetattr_spec);
1181 ADD("HeapCTypeSetattr", HeapCTypeSetattr);
1182
1183 PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
1184 if (subclass_with_finalizer_bases == NULL) {
1185 return -1;
1186 }
1187 PyObject *HeapCTypeSubclassWithFinalizer = PyType_FromSpecWithBases(
1188 &HeapCTypeSubclassWithFinalizer_spec, subclass_with_finalizer_bases);
1189 Py_DECREF(subclass_with_finalizer_bases);
1190 ADD("HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer);
1191
1192 PyObject *HeapCTypeMetaclass = PyType_FromMetaclass(
1193 &PyType_Type, m, &HeapCTypeMetaclass_spec, (PyObject *) &PyType_Type);
1194 ADD("HeapCTypeMetaclass", HeapCTypeMetaclass);
1195
1196 PyObject *HeapCTypeMetaclassCustomNew = PyType_FromMetaclass(
1197 &PyType_Type, m, &HeapCTypeMetaclassCustomNew_spec, (PyObject *) &PyType_Type);
1198 ADD("HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew);
1199
1200 PyObject *HeapCTypeMetaclassNullNew = PyType_FromMetaclass(
1201 &PyType_Type, m, &HeapCTypeMetaclassNullNew_spec, (PyObject *) &PyType_Type);
1202 ADD("HeapCTypeMetaclassNullNew", HeapCTypeMetaclassNullNew);
1203
1204 PyObject *HeapCCollection = PyType_FromMetaclass(
1205 NULL, m, &HeapCCollection_spec, NULL);
1206 if (HeapCCollection == NULL) {
1207 return -1;
1208 }
1209 int rc = PyModule_AddType(m, (PyTypeObject *)HeapCCollection);
1210 Py_DECREF(HeapCCollection);
1211 if (rc < 0) {
1212 return -1;
1213 }
1214
1215 return 0;
1216 }
1217