12
12
# define NAME _testcpp03ext
13
13
#endif
14
14
15
+ #define _STR (NAME ) #NAME
16
+ #define STR (NAME ) _STR(NAME)
17
+
15
18
PyDoc_STRVAR (_testcppext_add_doc,
16
19
" add(x, y)\n "
17
20
" \n "
@@ -123,11 +126,77 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
123
126
Py_RETURN_NONE;
124
127
}
125
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 ();
135
+ virtual ~VirtualPyObject () {
136
+ delete [] internal_data;
137
+ --instance_count;
138
+ }
139
+ virtual void set_internal_data () {
140
+ internal_data[0 ] = 1 ;
141
+ }
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
+
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 *
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
+ }
126
194
127
195
static PyMethodDef _testcppext_methods[] = {
128
196
{" add" , _testcppext_add, METH_VARARGS, _testcppext_add_doc},
129
197
{" test_api_casts" , test_api_casts, METH_NOARGS, _Py_NULL},
130
198
{" test_unicode" , test_unicode, METH_NOARGS, _Py_NULL},
199
+ {" test_virtual_object" , test_virtual_object, METH_NOARGS, _Py_NULL},
131
200
// Note: _testcppext_exec currently runs all test functions directly.
132
201
// When adding a new one, add a call there.
133
202
@@ -152,6 +221,10 @@ _testcppext_exec(PyObject *module)
152
221
if (!result) return -1 ;
153
222
Py_DECREF (result);
154
223
224
+ result = PyObject_CallMethod (module, " test_virtual_object" , " " );
225
+ if (!result) return -1 ;
226
+ Py_DECREF (result);
227
+
155
228
return 0 ;
156
229
}
157
230
@@ -163,9 +236,6 @@ static PyModuleDef_Slot _testcppext_slots[] = {
163
236
164
237
PyDoc_STRVAR (_testcppext_doc, " C++ test extension." );
165
238
166
- #define _STR (NAME ) #NAME
167
- #define STR (NAME ) _STR(NAME)
168
-
169
239
static struct PyModuleDef _testcppext_module = {
170
240
PyModuleDef_HEAD_INIT, // m_base
171
241
STR (NAME), // m_name
0 commit comments