1 // gh-91321: Very basic C++ test extension to check that the Python C API is
2 // compatible with C++ and does not emit C++ compiler warnings.
3
4 // Always enable assertions
5 #undef NDEBUG
6
7 #include "Python.h"
8
9 #if __cplusplus >= 201103
10 # define NAME _testcpp11ext
11 #else
12 # define NAME _testcpp03ext
13 #endif
14
15 #define _STR(NAME) #NAME
16 #define STR(NAME) _STR(NAME)
17
18 PyDoc_STRVAR(_testcppext_add_doc,
19 "add(x, y)\n"
20 "\n"
21 "Return the sum of two integers: x + y.");
22
23 static PyObject *
_testcppext_add(PyObject * Py_UNUSED (module),PyObject * args)24 _testcppext_add(PyObject *Py_UNUSED(module), PyObject *args)
25 {
26 long i, j;
27 if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) {
28 return _Py_NULL;
29 }
30 long res = i + j;
31 return PyLong_FromLong(res);
32 }
33
34
35 // Class to test operator casting an object to PyObject*
36 class StrongRef
37 {
38 public:
StrongRef(PyObject * obj)39 StrongRef(PyObject *obj) : m_obj(obj) {
40 Py_INCREF(this->m_obj);
41 }
42
~StrongRef()43 ~StrongRef() {
44 Py_DECREF(this->m_obj);
45 }
46
47 // Cast to PyObject*: get a borrowed reference
operator PyObject*() const48 inline operator PyObject*() const { return this->m_obj; }
49
50 private:
51 PyObject *m_obj; // Strong reference
52 };
53
54
55 static PyObject *
test_api_casts(PyObject * Py_UNUSED (module),PyObject * Py_UNUSED (args))56 test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
57 {
58 PyObject *obj = Py_BuildValue("(ii)", 1, 2);
59 if (obj == _Py_NULL) {
60 return _Py_NULL;
61 }
62 Py_ssize_t refcnt = Py_REFCNT(obj);
63 assert(refcnt >= 1);
64
65 // gh-92138: For backward compatibility, functions of Python C API accepts
66 // "const PyObject*". Check that using it does not emit C++ compiler
67 // warnings.
68 const PyObject *const_obj = obj;
69 Py_INCREF(const_obj);
70 Py_DECREF(const_obj);
71 PyTypeObject *type = Py_TYPE(const_obj);
72 assert(Py_REFCNT(const_obj) == refcnt);
73 assert(type == &PyTuple_Type);
74 assert(PyTuple_GET_SIZE(const_obj) == 2);
75 PyObject *one = PyTuple_GET_ITEM(const_obj, 0);
76 assert(PyLong_AsLong(one) == 1);
77
78 // gh-92898: StrongRef doesn't inherit from PyObject but has an operator to
79 // cast to PyObject*.
80 StrongRef strong_ref(obj);
81 assert(Py_TYPE(strong_ref) == &PyTuple_Type);
82 assert(Py_REFCNT(strong_ref) == (refcnt + 1));
83 Py_INCREF(strong_ref);
84 Py_DECREF(strong_ref);
85
86 // gh-93442: Pass 0 as NULL for PyObject*
87 Py_XINCREF(0);
88 Py_XDECREF(0);
89 #if _cplusplus >= 201103
90 // Test nullptr passed as PyObject*
91 Py_XINCREF(nullptr);
92 Py_XDECREF(nullptr);
93 #endif
94
95 Py_DECREF(obj);
96 Py_RETURN_NONE;
97 }
98
99
100 static PyObject *
test_unicode(PyObject * Py_UNUSED (module),PyObject * Py_UNUSED (args))101 test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
102 {
103 PyObject *str = PyUnicode_FromString("abc");
104 if (str == _Py_NULL) {
105 return _Py_NULL;
106 }
107
108 assert(PyUnicode_Check(str));
109 assert(PyUnicode_GET_LENGTH(str) == 3);
110
111 // gh-92800: test PyUnicode_READ()
112 const void* data = PyUnicode_DATA(str);
113 assert(data != _Py_NULL);
114 int kind = PyUnicode_KIND(str);
115 assert(kind == PyUnicode_1BYTE_KIND);
116 assert(PyUnicode_READ(kind, data, 0) == 'a');
117
118 // gh-92800: test PyUnicode_READ() casts
119 const void* const_data = PyUnicode_DATA(str);
120 unsigned int ukind = static_cast<unsigned int>(kind);
121 assert(PyUnicode_READ(ukind, const_data, 2) == 'c');
122
123 assert(PyUnicode_READ_CHAR(str, 1) == 'b');
124
125 Py_DECREF(str);
126 Py_RETURN_NONE;
127 }
128
129 /* Test a `new`-allocated object with a virtual method.
130 * (https://github.com/python/cpython/issues/94731) */
131
132 class VirtualPyObject : public PyObject {
133 public:
134 VirtualPyObject();
~VirtualPyObject()135 virtual ~VirtualPyObject() {
136 delete [] internal_data;
137 --instance_count;
138 }
set_internal_data()139 virtual void set_internal_data() {
140 internal_data[0] = 1;
141 }
dealloc(PyObject * o)142 static void dealloc(PyObject* o) {
143 delete static_cast<VirtualPyObject*>(o);
144 }
145
146 // Number of "living" instances
147 static int instance_count;
148 private:
149 // buffer that can get corrupted
150 int* internal_data;
151 };
152
153 int VirtualPyObject::instance_count = 0;
154
155 PyType_Slot VirtualPyObject_Slots[] = {
156 {Py_tp_free, (void*)VirtualPyObject::dealloc},
157 {0, _Py_NULL},
158 };
159
160 PyType_Spec VirtualPyObject_Spec = {
161 /* .name */ STR(NAME) ".VirtualPyObject",
162 /* .basicsize */ sizeof(VirtualPyObject),
163 /* .itemsize */ 0,
164 /* .flags */ Py_TPFLAGS_DEFAULT,
165 /* .slots */ VirtualPyObject_Slots,
166 };
167
VirtualPyObject()168 VirtualPyObject::VirtualPyObject() {
169 // Create a temporary type (just so we don't need to store it)
170 PyObject *type = PyType_FromSpec(&VirtualPyObject_Spec);
171 // no good way to signal failure from a C++ constructor, so use assert
172 // for error handling
173 assert(type);
174 assert(PyObject_Init(this, (PyTypeObject *)type));
175 Py_DECREF(type);
176 internal_data = new int[50];
177 ++instance_count;
178 }
179
180 static PyObject *
test_virtual_object(PyObject * Py_UNUSED (module),PyObject * Py_UNUSED (args))181 test_virtual_object(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
182 {
183 VirtualPyObject* obj = new VirtualPyObject();
184 obj->set_internal_data();
185 Py_DECREF(obj);
186 if (VirtualPyObject::instance_count != 0) {
187 return PyErr_Format(
188 PyExc_AssertionError,
189 "instance_count should be 0, got %d",
190 VirtualPyObject::instance_count);
191 }
192 Py_RETURN_NONE;
193 }
194
195 static PyMethodDef _testcppext_methods[] = {
196 {"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc},
197 {"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL},
198 {"test_unicode", test_unicode, METH_NOARGS, _Py_NULL},
199 {"test_virtual_object", test_virtual_object, METH_NOARGS, _Py_NULL},
200 // Note: _testcppext_exec currently runs all test functions directly.
201 // When adding a new one, add a call there.
202
203 {_Py_NULL, _Py_NULL, 0, _Py_NULL} /* sentinel */
204 };
205
206
207 static int
_testcppext_exec(PyObject * module)208 _testcppext_exec(PyObject *module)
209 {
210 if (PyModule_AddIntMacro(module, __cplusplus) < 0) {
211 return -1;
212 }
213
214 PyObject *result;
215
216 result = PyObject_CallMethod(module, "test_api_casts", "");
217 if (!result) return -1;
218 Py_DECREF(result);
219
220 result = PyObject_CallMethod(module, "test_unicode", "");
221 if (!result) return -1;
222 Py_DECREF(result);
223
224 result = PyObject_CallMethod(module, "test_virtual_object", "");
225 if (!result) return -1;
226 Py_DECREF(result);
227
228 return 0;
229 }
230
231 static PyModuleDef_Slot _testcppext_slots[] = {
232 {Py_mod_exec, reinterpret_cast<void*>(_testcppext_exec)},
233 {0, _Py_NULL}
234 };
235
236
237 PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");
238
239 static struct PyModuleDef _testcppext_module = {
240 PyModuleDef_HEAD_INIT, // m_base
241 STR(NAME), // m_name
242 _testcppext_doc, // m_doc
243 0, // m_size
244 _testcppext_methods, // m_methods
245 _testcppext_slots, // m_slots
246 _Py_NULL, // m_traverse
247 _Py_NULL, // m_clear
248 _Py_NULL, // m_free
249 };
250
251 #define _FUNC_NAME(NAME) PyInit_ ## NAME
252 #define FUNC_NAME(NAME) _FUNC_NAME(NAME)
253
254 PyMODINIT_FUNC
FUNC_NAME(NAME)255 FUNC_NAME(NAME)(void)
256 {
257 return PyModuleDef_Init(&_testcppext_module);
258 }
259