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 fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
12 {
13 if (args == Py_None) {
14 *stack = NULL;
15 *nargs = 0;
16 }
17 else if (PyTuple_Check(args)) {
18 *stack = ((PyTupleObject *)args)->ob_item;
19 *nargs = PyTuple_GET_SIZE(args);
20 }
21 else {
22 PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
23 return -1;
24 }
25 return 0;
26 }
27
28
29 static PyObject *
30 test_pyobject_fastcall(PyObject *self, PyObject *args)
31 {
32 PyObject *func, *func_args;
33 PyObject **stack;
34 Py_ssize_t nargs;
35
36 if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) {
37 return NULL;
38 }
39
40 if (fastcall_args(func_args, &stack, &nargs) < 0) {
41 return NULL;
42 }
43 return _PyObject_FastCall(func, stack, nargs);
44 }
45
46 static PyObject *
47 test_pyobject_fastcalldict(PyObject *self, PyObject *args)
48 {
49 PyObject *func, *func_args, *kwargs;
50 PyObject **stack;
51 Py_ssize_t nargs;
52
53 if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) {
54 return NULL;
55 }
56
57 if (fastcall_args(func_args, &stack, &nargs) < 0) {
58 return NULL;
59 }
60
61 if (kwargs == Py_None) {
62 kwargs = NULL;
63 }
64 else if (!PyDict_Check(kwargs)) {
65 PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
66 return NULL;
67 }
68
69 return PyObject_VectorcallDict(func, stack, nargs, kwargs);
70 }
71
72 static PyObject *
73 test_pyobject_vectorcall(PyObject *self, PyObject *args)
74 {
75 PyObject *func, *func_args, *kwnames = NULL;
76 PyObject **stack;
77 Py_ssize_t nargs, nkw;
78
79 if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) {
80 return NULL;
81 }
82
83 if (fastcall_args(func_args, &stack, &nargs) < 0) {
84 return NULL;
85 }
86
87 if (kwnames == Py_None) {
88 kwnames = NULL;
89 }
90 else if (PyTuple_Check(kwnames)) {
91 nkw = PyTuple_GET_SIZE(kwnames);
92 if (nargs < nkw) {
93 PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
94 return NULL;
95 }
96 nargs -= nkw;
97 }
98 else {
99 PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
100 return NULL;
101 }
102 return PyObject_Vectorcall(func, stack, nargs, kwnames);
103 }
104
105 static PyObject *
106 override_vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf,
107 PyObject *kwnames)
108 {
109 return PyUnicode_FromString("overridden");
110 }
111
112 static PyObject *
113 function_setvectorcall(PyObject *self, PyObject *func)
114 {
115 if (!PyFunction_Check(func)) {
116 PyErr_SetString(PyExc_TypeError, "'func' must be a function");
117 return NULL;
118 }
119 PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall);
120 Py_RETURN_NONE;
121 }
122
123 static PyObject *
124 test_pyvectorcall_call(PyObject *self, PyObject *args)
125 {
126 PyObject *func;
127 PyObject *argstuple;
128 PyObject *kwargs = NULL;
129
130 if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
131 return NULL;
132 }
133
134 if (!PyTuple_Check(argstuple)) {
135 PyErr_SetString(PyExc_TypeError, "args must be a tuple");
136 return NULL;
137 }
138 if (kwargs != NULL && !PyDict_Check(kwargs)) {
139 PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
140 return NULL;
141 }
142
143 return PyVectorcall_Call(func, argstuple, kwargs);
144 }
145
146 PyObject *
147 VectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
148 return PyUnicode_FromString("tp_call");
149 }
150
151 PyObject *
152 VectorCallClass_vectorcall(PyObject *callable,
153 PyObject *const *args,
154 size_t nargsf,
155 PyObject *kwnames) {
156 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 _testcapi_VectorCallClass_set_vectorcall_impl(PyObject *self,
176 PyTypeObject *type)
177 /*[clinic end generated code: output=b37f0466f15da903 input=840de66182c7d71a]*/
178 {
179 if (!PyObject_TypeCheck(self, type)) {
180 return PyErr_Format(
181 PyExc_TypeError,
182 "expected %s instance",
183 PyType_GetName(type));
184 }
185 if (!type->tp_vectorcall_offset) {
186 return PyErr_Format(
187 PyExc_TypeError,
188 "type %s has no vectorcall offset",
189 PyType_GetName(type));
190 }
191 *(vectorcallfunc*)((char*)self + type->tp_vectorcall_offset) = (
192 VectorCallClass_vectorcall);
193 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 _testcapi_make_vectorcall_class_impl(PyObject *module, PyTypeObject *base)
227 /*[clinic end generated code: output=16dcfc3062ddf968 input=f72e01ccf52de2b4]*/
228 {
229 if (!base) {
230 base = (PyTypeObject *)&PyBaseObject_Type;
231 }
232 VectorCallClass_members[0].offset = base->tp_basicsize;
233 PyType_Spec spec = {
234 .name = "_testcapi.VectorcallClass",
235 .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 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 _testcapi_has_vectorcall_flag_impl(PyObject *module, PyTypeObject *type)
256 /*[clinic end generated code: output=3ae8d1374388c671 input=8eee492ac548749e]*/
257 {
258 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 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 MethodDescriptorObject *md = (MethodDescriptorObject *)callable;
285 return PyBool_FromLong(md->vectorcall != NULL);
286 }
287
288 static PyObject *
289 MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw)
290 {
291 MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0);
292 op->vectorcall = MethodDescriptor_vectorcall;
293 return (PyObject *)op;
294 }
295
296 static PyObject *
297 func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
298 {
299 if (obj == Py_None || obj == NULL) {
300 return Py_NewRef(func);
301 }
302 return PyMethod_New(func, obj);
303 }
304
305 static PyObject *
306 nop_descr_get(PyObject *func, PyObject *obj, PyObject *type)
307 {
308 return Py_NewRef(func);
309 }
310
311 static PyObject *
312 call_return_args(PyObject *self, PyObject *args, PyObject *kwargs)
313 {
314 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 MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw)
350 {
351 MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type);
352 op->base.vectorcall = NULL;
353 op->vectorcall = MethodDescriptor_vectorcall;
354 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 _PyTestCapi_Init_Vectorcall(PyObject *m) {
370 if (PyModule_AddFunctions(m, TestMethods) < 0) {
371 return -1;
372 }
373
374 if (PyType_Ready(&MethodDescriptorBase_Type) < 0) {
375 return -1;
376 }
377 if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) {
378 return -1;
379 }
380
381 MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type;
382 if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) {
383 return -1;
384 }
385 if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) {
386 return -1;
387 }
388
389 MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type;
390 if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) {
391 return -1;
392 }
393 if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) {
394 return -1;
395 }
396
397 MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type;
398 if (PyType_Ready(&MethodDescriptor2_Type) < 0) {
399 return -1;
400 }
401 if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) {
402 return -1;
403 }
404
405 return 0;
406 }