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,76 @@ 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
+ .flags = Py_TPFLAGS_DEFAULT,
164
+ .slots = VirtualPyObject_Slots,
165
+ };
166
+
167
+ VirtualPyObject::VirtualPyObject () {
168
+ // Create a temporary type (just so we don't need to store it)
169
+ PyObject *ipotype = PyType_FromSpec (&VirtualPyObject_Spec);
170
+ // no good way to signal failure from a C++ constructor, so use assert
171
+ // for error handling
172
+ assert (ipotype);
173
+ assert (PyObject_Init (this , (PyTypeObject *)ipotype));
174
+ Py_DECREF (ipotype);
175
+ internal_data = new int [50 ];
176
+ ++instance_count;
177
+ }
178
+
179
+ static PyObject *
180
+ test_virtual_object (PyObject *Py_UNUSED (module), PyObject *Py_UNUSED(args))
181
+ {
182
+ VirtualPyObject* obj = new VirtualPyObject ();
183
+ obj->set_internal_data ();
184
+ Py_DECREF (obj);
185
+ if (VirtualPyObject::instance_count != 0 ) {
186
+ return PyErr_Format (
187
+ PyExc_AssertionError,
188
+ " instance_count should be 0, got %d" ,
189
+ VirtualPyObject::instance_count);
190
+ }
191
+ Py_RETURN_NONE;
192
+ }
126
193
127
194
static PyMethodDef _testcppext_methods[] = {
128
195
{" add" , _testcppext_add, METH_VARARGS, _testcppext_add_doc},
129
196
{" test_api_casts" , test_api_casts, METH_NOARGS, _Py_NULL},
130
197
{" test_unicode" , test_unicode, METH_NOARGS, _Py_NULL},
198
+ {" test_virtual_object" , test_virtual_object, METH_NOARGS, _Py_NULL},
131
199
// Note: _testcppext_exec currently runs all test functions directly.
132
200
// When adding a new one, add a call there.
133
201
@@ -152,6 +220,10 @@ _testcppext_exec(PyObject *module)
152
220
if (!result) return -1 ;
153
221
Py_DECREF (result);
154
222
223
+ result = PyObject_CallMethod (module, " test_virtual_object" , " " );
224
+ if (!result) return -1 ;
225
+ Py_DECREF (result);
226
+
155
227
return 0 ;
156
228
}
157
229
@@ -163,9 +235,6 @@ static PyModuleDef_Slot _testcppext_slots[] = {
163
235
164
236
PyDoc_STRVAR (_testcppext_doc, " C++ test extension." );
165
237
166
- #define _STR (NAME ) #NAME
167
- #define STR (NAME ) _STR(NAME)
168
-
169
238
static struct PyModuleDef _testcppext_module = {
170
239
PyModuleDef_HEAD_INIT, // m_base
171
240
STR (NAME), // m_name
0 commit comments