• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Need limited C API version 3.12 for PyType_FromMetaclass()
2 #include "pyconfig.h"   // Py_GIL_DISABLED
3 #if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
4 #  define Py_LIMITED_API 0x030c0000
5 #endif
6 
7 #include "parts.h"
8 #include <stddef.h>               // max_align_t
9 #include <string.h>               // memset
10 
11 static PyType_Slot empty_slots[] = {
12     {0, NULL},
13 };
14 
15 static PyObject *
make_sized_heaptypes(PyObject * module,PyObject * args)16 make_sized_heaptypes(PyObject *module, PyObject *args)
17 {
18     PyObject *base = NULL;
19     PyObject *sub = NULL;
20     PyObject *instance = NULL;
21     PyObject *result = NULL;
22 
23     int extra_base_size, basicsize;
24 
25     int r = PyArg_ParseTuple(args, "ii", &extra_base_size, &basicsize);
26     if (!r) {
27         goto finally;
28     }
29 
30     PyType_Spec base_spec = {
31         .name = "_testcapi.Base",
32         .basicsize = sizeof(PyObject) + extra_base_size,
33         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
34         .slots = empty_slots,
35     };
36     PyType_Spec sub_spec = {
37         .name = "_testcapi.Sub",
38         .basicsize = basicsize,
39         .flags = Py_TPFLAGS_DEFAULT,
40         .slots = empty_slots,
41     };
42 
43     base = PyType_FromMetaclass(NULL, module, &base_spec, NULL);
44     if (!base) {
45         goto finally;
46     }
47     sub = PyType_FromMetaclass(NULL, module, &sub_spec, base);
48     if (!sub) {
49         goto finally;
50     }
51     instance = PyObject_CallNoArgs(sub);
52     if (!instance) {
53         goto finally;
54     }
55     char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub);
56     if (!data_ptr) {
57         goto finally;
58     }
59     Py_ssize_t data_size = PyType_GetTypeDataSize((PyTypeObject *)sub);
60     if (data_size < 0) {
61         goto finally;
62     }
63 
64     result = Py_BuildValue("OOOKnn", base, sub, instance,
65                            (unsigned long long)data_ptr,
66                            (Py_ssize_t)(data_ptr - (char*)instance),
67                            data_size);
68   finally:
69     Py_XDECREF(base);
70     Py_XDECREF(sub);
71     Py_XDECREF(instance);
72     return result;
73 }
74 
75 static PyObject *
var_heaptype_set_data_to_3s(PyObject * self,PyTypeObject * defining_class,PyObject ** args,Py_ssize_t nargs,PyObject * kwnames)76 var_heaptype_set_data_to_3s(
77     PyObject *self, PyTypeObject *defining_class,
78     PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
79 {
80     void *data_ptr = PyObject_GetTypeData(self, defining_class);
81     if (!data_ptr) {
82         return NULL;
83     }
84     Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class);
85     if (data_size < 0) {
86         return NULL;
87     }
88     memset(data_ptr, 3, data_size);
89     Py_RETURN_NONE;
90 }
91 
92 static PyObject *
var_heaptype_get_data(PyObject * self,PyTypeObject * defining_class,PyObject ** args,Py_ssize_t nargs,PyObject * kwnames)93 var_heaptype_get_data(PyObject *self, PyTypeObject *defining_class,
94                       PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
95 {
96     void *data_ptr = PyObject_GetTypeData(self, defining_class);
97     if (!data_ptr) {
98         return NULL;
99     }
100     Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class);
101     if (data_size < 0) {
102         return NULL;
103     }
104     return PyBytes_FromStringAndSize(data_ptr, data_size);
105 }
106 
107 static PyMethodDef var_heaptype_methods[] = {
108     {"set_data_to_3s", _PyCFunction_CAST(var_heaptype_set_data_to_3s),
109         METH_METHOD | METH_FASTCALL | METH_KEYWORDS},
110     {"get_data", _PyCFunction_CAST(var_heaptype_get_data),
111         METH_METHOD | METH_FASTCALL | METH_KEYWORDS},
112     {NULL},
113 };
114 
115 static PyObject *
subclass_var_heaptype(PyObject * module,PyObject * args)116 subclass_var_heaptype(PyObject *module, PyObject *args)
117 {
118     PyObject *result = NULL;
119 
120     PyObject *base; // borrowed from args
121     int basicsize, itemsize;
122     long pfunc;
123 
124     int r = PyArg_ParseTuple(args, "Oiil", &base, &basicsize, &itemsize, &pfunc);
125     if (!r) {
126         goto finally;
127     }
128 
129     PyType_Slot slots[] = {
130         {Py_tp_methods, var_heaptype_methods},
131         {0, NULL},
132     };
133 
134     PyType_Spec sub_spec = {
135         .name = "_testcapi.Sub",
136         .basicsize = basicsize,
137         .itemsize = itemsize,
138         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_ITEMS_AT_END,
139         .slots = slots,
140     };
141 
142     result = PyType_FromMetaclass(NULL, module, &sub_spec, base);
143   finally:
144     return result;
145 }
146 
147 static PyObject *
subclass_heaptype(PyObject * module,PyObject * args)148 subclass_heaptype(PyObject *module, PyObject *args)
149 {
150     PyObject *result = NULL;
151 
152     PyObject *base; // borrowed from args
153     int basicsize, itemsize;
154 
155     int r = PyArg_ParseTuple(args, "Oii", &base, &basicsize, &itemsize);
156     if (!r) {
157         goto finally;
158     }
159 
160     PyType_Slot slots[] = {
161         {Py_tp_methods, var_heaptype_methods},
162         {0, NULL},
163     };
164 
165     PyType_Spec sub_spec = {
166         .name = "_testcapi.Sub",
167         .basicsize = basicsize,
168         .itemsize = itemsize,
169         .flags = Py_TPFLAGS_DEFAULT,
170         .slots = slots,
171     };
172 
173     result = PyType_FromMetaclass(NULL, module, &sub_spec, base);
174   finally:
175     return result;
176 }
177 
178 static PyMemberDef *
heaptype_with_member_extract_and_check_memb(PyObject * self)179 heaptype_with_member_extract_and_check_memb(PyObject *self)
180 {
181     PyMemberDef *def = PyType_GetSlot(Py_TYPE(self), Py_tp_members);
182     if (!def) {
183         if (!PyErr_Occurred()) {
184             PyErr_SetString(PyExc_ValueError, "tp_members is NULL");
185         }
186         return NULL;
187     }
188     if (!def[0].name) {
189         PyErr_SetString(PyExc_ValueError, "tp_members[0] is NULL");
190         return NULL;
191     }
192     if (def[1].name) {
193         PyErr_SetString(PyExc_ValueError, "tp_members[1] is not NULL");
194         return NULL;
195     }
196     if (strcmp(def[0].name, "memb")) {
197         PyErr_SetString(PyExc_ValueError, "tp_members[0] is not for `memb`");
198         return NULL;
199     }
200     if (def[0].flags) {
201         PyErr_SetString(PyExc_ValueError, "tp_members[0] has flags set");
202         return NULL;
203     }
204     return def;
205 }
206 
207 static PyObject *
heaptype_with_member_get_memb(PyObject * self,PyObject * Py_UNUSED (ignored))208 heaptype_with_member_get_memb(PyObject *self, PyObject *Py_UNUSED(ignored))
209 {
210     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self);
211     return PyMember_GetOne((const char *)self, def);
212 }
213 
214 static PyObject *
heaptype_with_member_set_memb(PyObject * self,PyObject * value)215 heaptype_with_member_set_memb(PyObject *self, PyObject *value)
216 {
217     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self);
218     int r = PyMember_SetOne((char *)self, def, value);
219     if (r < 0) {
220         return NULL;
221     }
222     Py_RETURN_NONE;
223 }
224 
225 static PyObject *
get_memb_offset(PyObject * self,PyObject * Py_UNUSED (ignored))226 get_memb_offset(PyObject *self, PyObject *Py_UNUSED(ignored))
227 {
228     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self);
229     return PyLong_FromSsize_t(def->offset);
230 }
231 
232 static PyObject *
heaptype_with_member_get_memb_relative(PyObject * self,PyObject * Py_UNUSED (ignored))233 heaptype_with_member_get_memb_relative(PyObject *self, PyObject *Py_UNUSED(ignored))
234 {
235     PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET};
236     return PyMember_GetOne((const char *)self, &def);
237 }
238 
239 static PyObject *
heaptype_with_member_set_memb_relative(PyObject * self,PyObject * value)240 heaptype_with_member_set_memb_relative(PyObject *self, PyObject *value)
241 {
242     PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET};
243     int r = PyMember_SetOne((char *)self, &def, value);
244     if (r < 0) {
245         return NULL;
246     }
247     Py_RETURN_NONE;
248 }
249 
250 static PyMethodDef heaptype_with_member_methods[] = {
251     {"get_memb", heaptype_with_member_get_memb, METH_NOARGS},
252     {"set_memb", heaptype_with_member_set_memb, METH_O},
253     {"get_memb_offset", get_memb_offset, METH_NOARGS},
254     {"get_memb_relative", heaptype_with_member_get_memb_relative, METH_NOARGS},
255     {"set_memb_relative", heaptype_with_member_set_memb_relative, METH_O},
256     {NULL},
257 };
258 
259 static PyObject *
make_heaptype_with_member(PyObject * module,PyObject * args)260 make_heaptype_with_member(PyObject *module, PyObject *args)
261 {
262     PyObject *base = NULL;
263     PyObject *result = NULL;
264 
265     int extra_base_size, basicsize, offset, add_flag;
266 
267     int r = PyArg_ParseTuple(args, "iiip", &extra_base_size, &basicsize, &offset, &add_flag);
268     if (!r) {
269         goto finally;
270     }
271 
272     PyType_Spec base_spec = {
273         .name = "_testcapi.Base",
274         .basicsize = sizeof(PyObject) + extra_base_size,
275         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
276         .slots = empty_slots,
277     };
278     base = PyType_FromMetaclass(NULL, module, &base_spec, NULL);
279     if (!base) {
280         goto finally;
281     }
282 
283     PyMemberDef members[] = {
284         {"memb", Py_T_BYTE, offset, add_flag ? Py_RELATIVE_OFFSET : 0},
285         {0},
286     };
287     PyType_Slot slots[] = {
288         {Py_tp_members, members},
289         {Py_tp_methods, heaptype_with_member_methods},
290         {0, NULL},
291     };
292 
293     PyType_Spec sub_spec = {
294         .name = "_testcapi.Sub",
295         .basicsize = basicsize,
296         .flags = Py_TPFLAGS_DEFAULT,
297         .slots = slots,
298     };
299 
300     result = PyType_FromMetaclass(NULL, module, &sub_spec, base);
301   finally:
302     Py_XDECREF(base);
303     return result;
304 }
305 
306 
307 static PyObject *
test_alignof_max_align_t(PyObject * module,PyObject * Py_UNUSED (ignored))308 test_alignof_max_align_t(PyObject *module, PyObject *Py_UNUSED(ignored))
309 {
310     // We define ALIGNOF_MAX_ALIGN_T even if the compiler doesn't support
311     // max_align_t. Double-check that it's correct.
312     assert(ALIGNOF_MAX_ALIGN_T > 0);
313     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long long));
314     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long double));
315     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void*));
316     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void (*)(void)));
317 
318     // Ensure it's a power of two
319     assert((ALIGNOF_MAX_ALIGN_T & (ALIGNOF_MAX_ALIGN_T - 1)) == 0);
320 
321     Py_RETURN_NONE;
322 }
323 
324 static PyMethodDef TestMethods[] = {
325     {"make_sized_heaptypes", make_sized_heaptypes, METH_VARARGS},
326     {"subclass_var_heaptype", subclass_var_heaptype, METH_VARARGS},
327     {"subclass_heaptype", subclass_heaptype, METH_VARARGS},
328     {"make_heaptype_with_member", make_heaptype_with_member, METH_VARARGS},
329     {"test_alignof_max_align_t", test_alignof_max_align_t, METH_NOARGS},
330     {NULL},
331 };
332 
333 int
_PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject * m)334 _PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject *m)
335 {
336     if (PyModule_AddFunctions(m, TestMethods) < 0) {
337         return -1;
338     }
339 
340     if (PyModule_AddIntMacro(m, ALIGNOF_MAX_ALIGN_T) < 0) {
341         return -1;
342     }
343 
344     return 0;
345 }
346