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