Branch data Line data Source code
1 : : #include "parts.h"
2 : : #include "clinic/vectorcall.c.h"
3 : :
4 : : #include "structmember.h" // PyMemberDef
5 : : #include <stddef.h> // offsetof
6 : :
7 : :
8 : : /* Test PEP 590 - Vectorcall */
9 : :
10 : : static int
11 : 0 : fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
12 : : {
13 [ # # ]: 0 : if (args == Py_None) {
14 : 0 : *stack = NULL;
15 : 0 : *nargs = 0;
16 : : }
17 [ # # ]: 0 : else if (PyTuple_Check(args)) {
18 : 0 : *stack = ((PyTupleObject *)args)->ob_item;
19 : 0 : *nargs = PyTuple_GET_SIZE(args);
20 : : }
21 : : else {
22 : 0 : PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
23 : 0 : return -1;
24 : : }
25 : 0 : return 0;
26 : : }
27 : :
28 : :
29 : : static PyObject *
30 : 0 : test_pyobject_fastcall(PyObject *self, PyObject *args)
31 : : {
32 : : PyObject *func, *func_args;
33 : : PyObject **stack;
34 : : Py_ssize_t nargs;
35 : :
36 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) {
37 : 0 : return NULL;
38 : : }
39 : :
40 [ # # ]: 0 : if (fastcall_args(func_args, &stack, &nargs) < 0) {
41 : 0 : return NULL;
42 : : }
43 : 0 : return _PyObject_FastCall(func, stack, nargs);
44 : : }
45 : :
46 : : static PyObject *
47 : 0 : test_pyobject_fastcalldict(PyObject *self, PyObject *args)
48 : : {
49 : : PyObject *func, *func_args, *kwargs;
50 : : PyObject **stack;
51 : : Py_ssize_t nargs;
52 : :
53 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) {
54 : 0 : return NULL;
55 : : }
56 : :
57 [ # # ]: 0 : if (fastcall_args(func_args, &stack, &nargs) < 0) {
58 : 0 : return NULL;
59 : : }
60 : :
61 [ # # ]: 0 : if (kwargs == Py_None) {
62 : 0 : kwargs = NULL;
63 : : }
64 [ # # ]: 0 : else if (!PyDict_Check(kwargs)) {
65 : 0 : PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
66 : 0 : return NULL;
67 : : }
68 : :
69 : 0 : return PyObject_VectorcallDict(func, stack, nargs, kwargs);
70 : : }
71 : :
72 : : static PyObject *
73 : 0 : test_pyobject_vectorcall(PyObject *self, PyObject *args)
74 : : {
75 : 0 : PyObject *func, *func_args, *kwnames = NULL;
76 : : PyObject **stack;
77 : : Py_ssize_t nargs, nkw;
78 : :
79 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) {
80 : 0 : return NULL;
81 : : }
82 : :
83 [ # # ]: 0 : if (fastcall_args(func_args, &stack, &nargs) < 0) {
84 : 0 : return NULL;
85 : : }
86 : :
87 [ # # ]: 0 : if (kwnames == Py_None) {
88 : 0 : kwnames = NULL;
89 : : }
90 [ # # ]: 0 : else if (PyTuple_Check(kwnames)) {
91 : 0 : nkw = PyTuple_GET_SIZE(kwnames);
92 [ # # ]: 0 : if (nargs < nkw) {
93 : 0 : PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
94 : 0 : return NULL;
95 : : }
96 : 0 : nargs -= nkw;
97 : : }
98 : : else {
99 : 0 : PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
100 : 0 : return NULL;
101 : : }
102 : 0 : return PyObject_Vectorcall(func, stack, nargs, kwnames);
103 : : }
104 : :
105 : : static PyObject *
106 : 0 : override_vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf,
107 : : PyObject *kwnames)
108 : : {
109 : 0 : return PyUnicode_FromString("overridden");
110 : : }
111 : :
112 : : static PyObject *
113 : 0 : function_setvectorcall(PyObject *self, PyObject *func)
114 : : {
115 [ # # ]: 0 : if (!PyFunction_Check(func)) {
116 : 0 : PyErr_SetString(PyExc_TypeError, "'func' must be a function");
117 : 0 : return NULL;
118 : : }
119 : 0 : PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall);
120 : 0 : Py_RETURN_NONE;
121 : : }
122 : :
123 : : static PyObject *
124 : 0 : test_pyvectorcall_call(PyObject *self, PyObject *args)
125 : : {
126 : : PyObject *func;
127 : : PyObject *argstuple;
128 : 0 : PyObject *kwargs = NULL;
129 : :
130 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
131 : 0 : return NULL;
132 : : }
133 : :
134 [ # # ]: 0 : if (!PyTuple_Check(argstuple)) {
135 : 0 : PyErr_SetString(PyExc_TypeError, "args must be a tuple");
136 : 0 : return NULL;
137 : : }
138 [ # # # # ]: 0 : if (kwargs != NULL && !PyDict_Check(kwargs)) {
139 : 0 : PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
140 : 0 : return NULL;
141 : : }
142 : :
143 : 0 : return PyVectorcall_Call(func, argstuple, kwargs);
144 : : }
145 : :
146 : : PyObject *
147 : 0 : VectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
148 : 0 : return PyUnicode_FromString("tp_call");
149 : : }
150 : :
151 : : PyObject *
152 : 0 : VectorCallClass_vectorcall(PyObject *callable,
153 : : PyObject *const *args,
154 : : size_t nargsf,
155 : : PyObject *kwnames) {
156 : 0 : return PyUnicode_FromString("vectorcall");
157 : : }
158 : :
159 : : /*[clinic input]
160 : : module _testcapi
161 : : class _testcapi.VectorCallClass "PyObject *" "&PyType_Type"
162 : : [clinic start generated code]*/
163 : : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=8423a8e919f2f0df]*/
164 : :
165 : : /*[clinic input]
166 : : _testcapi.VectorCallClass.set_vectorcall
167 : :
168 : : type: object(subclass_of="&PyType_Type", type="PyTypeObject *")
169 : : /
170 : :
171 : : Set self's vectorcall function for `type` to one that returns "vectorcall"
172 : : [clinic start generated code]*/
173 : :
174 : : static PyObject *
175 : 0 : _testcapi_VectorCallClass_set_vectorcall_impl(PyObject *self,
176 : : PyTypeObject *type)
177 : : /*[clinic end generated code: output=b37f0466f15da903 input=840de66182c7d71a]*/
178 : : {
179 [ # # ]: 0 : if (!PyObject_TypeCheck(self, type)) {
180 : 0 : return PyErr_Format(
181 : : PyExc_TypeError,
182 : : "expected %s instance",
183 : : PyType_GetName(type));
184 : : }
185 [ # # ]: 0 : if (!type->tp_vectorcall_offset) {
186 : 0 : return PyErr_Format(
187 : : PyExc_TypeError,
188 : : "type %s has no vectorcall offset",
189 : : PyType_GetName(type));
190 : : }
191 : 0 : *(vectorcallfunc*)((char*)self + type->tp_vectorcall_offset) = (
192 : : VectorCallClass_vectorcall);
193 : 0 : Py_RETURN_NONE;
194 : : }
195 : :
196 : : PyMethodDef VectorCallClass_methods[] = {
197 : : _TESTCAPI_VECTORCALLCLASS_SET_VECTORCALL_METHODDEF
198 : : {NULL, NULL}
199 : : };
200 : :
201 : : PyMemberDef VectorCallClass_members[] = {
202 : : {"__vectorcalloffset__", T_PYSSIZET, 0/* set later */, READONLY},
203 : : {NULL}
204 : : };
205 : :
206 : : PyType_Slot VectorCallClass_slots[] = {
207 : : {Py_tp_call, VectorCallClass_tpcall},
208 : : {Py_tp_members, VectorCallClass_members},
209 : : {Py_tp_methods, VectorCallClass_methods},
210 : : {0},
211 : : };
212 : :
213 : : /*[clinic input]
214 : : _testcapi.make_vectorcall_class
215 : :
216 : : base: object(subclass_of="&PyType_Type", type="PyTypeObject *") = NULL
217 : : /
218 : :
219 : : Create a class whose instances return "tpcall" when called.
220 : :
221 : : When the "set_vectorcall" method is called on an instance, a vectorcall
222 : : function that returns "vectorcall" will be installed.
223 : : [clinic start generated code]*/
224 : :
225 : : static PyObject *
226 : 0 : _testcapi_make_vectorcall_class_impl(PyObject *module, PyTypeObject *base)
227 : : /*[clinic end generated code: output=16dcfc3062ddf968 input=f72e01ccf52de2b4]*/
228 : : {
229 [ # # ]: 0 : if (!base) {
230 : 0 : base = (PyTypeObject *)&PyBaseObject_Type;
231 : : }
232 : 0 : VectorCallClass_members[0].offset = base->tp_basicsize;
233 : 0 : PyType_Spec spec = {
234 : : .name = "_testcapi.VectorcallClass",
235 : 0 : .basicsize = (int)(base->tp_basicsize + sizeof(vectorcallfunc)),
236 : : .flags = Py_TPFLAGS_DEFAULT
237 : : | Py_TPFLAGS_HAVE_VECTORCALL
238 : : | Py_TPFLAGS_BASETYPE,
239 : : .slots = VectorCallClass_slots,
240 : : };
241 : :
242 : 0 : return PyType_FromSpecWithBases(&spec, (PyObject *)base);
243 : : }
244 : :
245 : : /*[clinic input]
246 : : _testcapi.has_vectorcall_flag -> bool
247 : :
248 : : type: object(subclass_of="&PyType_Type", type="PyTypeObject *")
249 : : /
250 : :
251 : : Return true iff Py_TPFLAGS_HAVE_VECTORCALL is set on the class.
252 : : [clinic start generated code]*/
253 : :
254 : : static int
255 : 0 : _testcapi_has_vectorcall_flag_impl(PyObject *module, PyTypeObject *type)
256 : : /*[clinic end generated code: output=3ae8d1374388c671 input=8eee492ac548749e]*/
257 : : {
258 : 0 : return PyType_HasFeature(type, Py_TPFLAGS_HAVE_VECTORCALL);
259 : : }
260 : :
261 : : static PyMethodDef TestMethods[] = {
262 : : {"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
263 : : {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
264 : : {"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
265 : : {"function_setvectorcall", function_setvectorcall, METH_O},
266 : : {"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
267 : : _TESTCAPI_MAKE_VECTORCALL_CLASS_METHODDEF
268 : : _TESTCAPI_HAS_VECTORCALL_FLAG_METHODDEF
269 : : {NULL},
270 : : };
271 : :
272 : :
273 : : typedef struct {
274 : : PyObject_HEAD
275 : : vectorcallfunc vectorcall;
276 : : } MethodDescriptorObject;
277 : :
278 : : static PyObject *
279 : 0 : MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args,
280 : : size_t nargsf, PyObject *kwnames)
281 : : {
282 : : /* True if using the vectorcall function in MethodDescriptorObject
283 : : * but False for MethodDescriptor2Object */
284 : 0 : MethodDescriptorObject *md = (MethodDescriptorObject *)callable;
285 : 0 : return PyBool_FromLong(md->vectorcall != NULL);
286 : : }
287 : :
288 : : static PyObject *
289 : 0 : MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw)
290 : : {
291 : 0 : MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0);
292 : 0 : op->vectorcall = MethodDescriptor_vectorcall;
293 : 0 : return (PyObject *)op;
294 : : }
295 : :
296 : : static PyObject *
297 : 0 : func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
298 : : {
299 [ # # # # ]: 0 : if (obj == Py_None || obj == NULL) {
300 : 0 : return Py_NewRef(func);
301 : : }
302 : 0 : return PyMethod_New(func, obj);
303 : : }
304 : :
305 : : static PyObject *
306 : 0 : nop_descr_get(PyObject *func, PyObject *obj, PyObject *type)
307 : : {
308 : 0 : return Py_NewRef(func);
309 : : }
310 : :
311 : : static PyObject *
312 : 0 : call_return_args(PyObject *self, PyObject *args, PyObject *kwargs)
313 : : {
314 : 0 : return Py_NewRef(args);
315 : : }
316 : :
317 : : static PyTypeObject MethodDescriptorBase_Type = {
318 : : PyVarObject_HEAD_INIT(NULL, 0)
319 : : "MethodDescriptorBase",
320 : : sizeof(MethodDescriptorObject),
321 : : .tp_new = MethodDescriptor_new,
322 : : .tp_call = PyVectorcall_Call,
323 : : .tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall),
324 : : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
325 : : Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL,
326 : : .tp_descr_get = func_descr_get,
327 : : };
328 : :
329 : : static PyTypeObject MethodDescriptorDerived_Type = {
330 : : PyVarObject_HEAD_INIT(NULL, 0)
331 : : "MethodDescriptorDerived",
332 : : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
333 : : };
334 : :
335 : : static PyTypeObject MethodDescriptorNopGet_Type = {
336 : : PyVarObject_HEAD_INIT(NULL, 0)
337 : : "MethodDescriptorNopGet",
338 : : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
339 : : .tp_call = call_return_args,
340 : : .tp_descr_get = nop_descr_get,
341 : : };
342 : :
343 : : typedef struct {
344 : : MethodDescriptorObject base;
345 : : vectorcallfunc vectorcall;
346 : : } MethodDescriptor2Object;
347 : :
348 : : static PyObject *
349 : 0 : MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw)
350 : : {
351 : 0 : MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type);
352 : 0 : op->base.vectorcall = NULL;
353 : 0 : op->vectorcall = MethodDescriptor_vectorcall;
354 : 0 : return (PyObject *)op;
355 : : }
356 : :
357 : : static PyTypeObject MethodDescriptor2_Type = {
358 : : PyVarObject_HEAD_INIT(NULL, 0)
359 : : "MethodDescriptor2",
360 : : sizeof(MethodDescriptor2Object),
361 : : .tp_new = MethodDescriptor2_new,
362 : : .tp_call = PyVectorcall_Call,
363 : : .tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall),
364 : : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL,
365 : : };
366 : :
367 : :
368 : : int
369 : 2 : _PyTestCapi_Init_Vectorcall(PyObject *m) {
370 [ - + ]: 2 : if (PyModule_AddFunctions(m, TestMethods) < 0) {
371 : 0 : return -1;
372 : : }
373 : :
374 [ - + ]: 2 : if (PyType_Ready(&MethodDescriptorBase_Type) < 0) {
375 : 0 : return -1;
376 : : }
377 [ - + ]: 2 : if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) {
378 : 0 : return -1;
379 : : }
380 : :
381 : 2 : MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type;
382 [ - + ]: 2 : if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) {
383 : 0 : return -1;
384 : : }
385 [ - + ]: 2 : if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) {
386 : 0 : return -1;
387 : : }
388 : :
389 : 2 : MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type;
390 [ - + ]: 2 : if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) {
391 : 0 : return -1;
392 : : }
393 [ - + ]: 2 : if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) {
394 : 0 : return -1;
395 : : }
396 : :
397 : 2 : MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type;
398 [ - + ]: 2 : if (PyType_Ready(&MethodDescriptor2_Type) < 0) {
399 : 0 : return -1;
400 : : }
401 [ - + ]: 2 : if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) {
402 : 0 : return -1;
403 : : }
404 : :
405 : 2 : return 0;
406 : : }
|