• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // clinic/vectorcall.c.h uses internal pycore_modsupport.h API
2 #define PYTESTCAPI_NEED_INTERNAL_API
3 
4 #include "parts.h"
5 #include "clinic/vectorcall.c.h"
6 
7 
8 #include <stddef.h>                 // offsetof
9 
10 /*[clinic input]
11 module _testcapi
12 [clinic start generated code]*/
13 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
14 
15 /* Test PEP 590 - Vectorcall */
16 
17 static int
fastcall_args(PyObject * args,PyObject *** stack,Py_ssize_t * nargs)18 fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
19 {
20     if (args == Py_None) {
21         *stack = NULL;
22         *nargs = 0;
23     }
24     else if (PyTuple_Check(args)) {
25         *stack = ((PyTupleObject *)args)->ob_item;
26         *nargs = PyTuple_GET_SIZE(args);
27     }
28     else {
29         PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
30         return -1;
31     }
32     return 0;
33 }
34 
35 /*[clinic input]
36 _testcapi.pyobject_fastcalldict
37     func: object
38     func_args: object
39     kwargs: object
40     /
41 [clinic start generated code]*/
42 
43 static PyObject *
_testcapi_pyobject_fastcalldict_impl(PyObject * module,PyObject * func,PyObject * func_args,PyObject * kwargs)44 _testcapi_pyobject_fastcalldict_impl(PyObject *module, PyObject *func,
45                                      PyObject *func_args, PyObject *kwargs)
46 /*[clinic end generated code: output=35902ece94de4418 input=b9c0196ca7d5f9e4]*/
47 {
48     PyObject **stack;
49     Py_ssize_t nargs;
50 
51     if (fastcall_args(func_args, &stack, &nargs) < 0) {
52         return NULL;
53     }
54 
55     if (kwargs == Py_None) {
56         kwargs = NULL;
57     }
58     else if (!PyDict_Check(kwargs)) {
59         PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
60         return NULL;
61     }
62 
63     return PyObject_VectorcallDict(func, stack, nargs, kwargs);
64 }
65 
66 /*[clinic input]
67 _testcapi.pyobject_vectorcall
68     func: object
69     func_args: object
70     kwnames: object
71     /
72 [clinic start generated code]*/
73 
74 static PyObject *
_testcapi_pyobject_vectorcall_impl(PyObject * module,PyObject * func,PyObject * func_args,PyObject * kwnames)75 _testcapi_pyobject_vectorcall_impl(PyObject *module, PyObject *func,
76                                    PyObject *func_args, PyObject *kwnames)
77 /*[clinic end generated code: output=ff77245bc6afe0d8 input=a0668dfef625764c]*/
78 {
79     PyObject **stack;
80     Py_ssize_t nargs, nkw;
81 
82     if (fastcall_args(func_args, &stack, &nargs) < 0) {
83         return NULL;
84     }
85 
86     if (kwnames == Py_None) {
87         kwnames = NULL;
88     }
89     else if (PyTuple_Check(kwnames)) {
90         nkw = PyTuple_GET_SIZE(kwnames);
91         if (nargs < nkw) {
92             PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
93             return NULL;
94         }
95         nargs -= nkw;
96     }
97     else {
98         PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
99         return NULL;
100     }
101     return PyObject_Vectorcall(func, stack, nargs, kwnames);
102 }
103 
104 static PyObject *
override_vectorcall(PyObject * callable,PyObject * const * args,size_t nargsf,PyObject * kwnames)105 override_vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf,
106                     PyObject *kwnames)
107 {
108     return PyUnicode_FromString("overridden");
109 }
110 
111 static PyObject *
function_setvectorcall(PyObject * self,PyObject * func)112 function_setvectorcall(PyObject *self, PyObject *func)
113 {
114     if (!PyFunction_Check(func)) {
115         PyErr_SetString(PyExc_TypeError, "'func' must be a function");
116         return NULL;
117     }
118     PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall);
119     Py_RETURN_NONE;
120 }
121 
122 /*[clinic input]
123 _testcapi.pyvectorcall_call
124     func: object
125     argstuple: object
126     kwargs: object = NULL
127     /
128 [clinic start generated code]*/
129 
130 static PyObject *
_testcapi_pyvectorcall_call_impl(PyObject * module,PyObject * func,PyObject * argstuple,PyObject * kwargs)131 _testcapi_pyvectorcall_call_impl(PyObject *module, PyObject *func,
132                                  PyObject *argstuple, PyObject *kwargs)
133 /*[clinic end generated code: output=809046fe78511306 input=4376ee7cabd698ce]*/
134 {
135     if (!PyTuple_Check(argstuple)) {
136         PyErr_SetString(PyExc_TypeError, "args must be a tuple");
137         return NULL;
138     }
139     if (kwargs != NULL && !PyDict_Check(kwargs)) {
140         PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
141         return NULL;
142     }
143 
144     return PyVectorcall_Call(func, argstuple, kwargs);
145 }
146 
147 PyObject *
VectorCallClass_tpcall(PyObject * self,PyObject * args,PyObject * kwargs)148 VectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
149     return PyUnicode_FromString("tp_call");
150 }
151 
152 PyObject *
VectorCallClass_vectorcall(PyObject * callable,PyObject * const * args,size_t nargsf,PyObject * kwnames)153 VectorCallClass_vectorcall(PyObject *callable,
154                             PyObject *const *args,
155                             size_t nargsf,
156                             PyObject *kwnames) {
157     return PyUnicode_FromString("vectorcall");
158 }
159 
160 /*[clinic input]
161 class _testcapi.VectorCallClass "PyObject *" "&PyType_Type"
162 [clinic start generated code]*/
163 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=95c63c1a47f9a995]*/
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 *
_testcapi_VectorCallClass_set_vectorcall_impl(PyObject * self,PyTypeObject * type)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__", Py_T_PYSSIZET, 0/* set later */, Py_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 *
_testcapi_make_vectorcall_class_impl(PyObject * module,PyTypeObject * base)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
_testcapi_has_vectorcall_flag_impl(PyObject * module,PyTypeObject * type)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     _TESTCAPI_PYOBJECT_FASTCALLDICT_METHODDEF
263     _TESTCAPI_PYOBJECT_VECTORCALL_METHODDEF
264     {"function_setvectorcall", function_setvectorcall, METH_O},
265     _TESTCAPI_PYVECTORCALL_CALL_METHODDEF
266     _TESTCAPI_MAKE_VECTORCALL_CLASS_METHODDEF
267     _TESTCAPI_HAS_VECTORCALL_FLAG_METHODDEF
268     {NULL},
269 };
270 
271 
272 typedef struct {
273     PyObject_HEAD
274     vectorcallfunc vectorcall;
275 } MethodDescriptorObject;
276 
277 static PyObject *
MethodDescriptor_vectorcall(PyObject * callable,PyObject * const * args,size_t nargsf,PyObject * kwnames)278 MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args,
279                             size_t nargsf, PyObject *kwnames)
280 {
281     /* True if using the vectorcall function in MethodDescriptorObject
282      * but False for MethodDescriptor2Object */
283     MethodDescriptorObject *md = (MethodDescriptorObject *)callable;
284     return PyBool_FromLong(md->vectorcall != NULL);
285 }
286 
287 static PyObject *
MethodDescriptor_new(PyTypeObject * type,PyObject * args,PyObject * kw)288 MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw)
289 {
290     MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0);
291     op->vectorcall = MethodDescriptor_vectorcall;
292     return (PyObject *)op;
293 }
294 
295 static PyObject *
func_descr_get(PyObject * func,PyObject * obj,PyObject * type)296 func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
297 {
298     if (obj == Py_None || obj == NULL) {
299         return Py_NewRef(func);
300     }
301     return PyMethod_New(func, obj);
302 }
303 
304 static PyObject *
nop_descr_get(PyObject * func,PyObject * obj,PyObject * type)305 nop_descr_get(PyObject *func, PyObject *obj, PyObject *type)
306 {
307     return Py_NewRef(func);
308 }
309 
310 static PyObject *
call_return_args(PyObject * self,PyObject * args,PyObject * kwargs)311 call_return_args(PyObject *self, PyObject *args, PyObject *kwargs)
312 {
313     return Py_NewRef(args);
314 }
315 
316 static PyTypeObject MethodDescriptorBase_Type = {
317     PyVarObject_HEAD_INIT(NULL, 0)
318     "MethodDescriptorBase",
319     sizeof(MethodDescriptorObject),
320     .tp_new = MethodDescriptor_new,
321     .tp_call = PyVectorcall_Call,
322     .tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall),
323     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
324                 Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL,
325     .tp_descr_get = func_descr_get,
326 };
327 
328 static PyTypeObject MethodDescriptorDerived_Type = {
329     PyVarObject_HEAD_INIT(NULL, 0)
330     "MethodDescriptorDerived",
331     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
332 };
333 
334 static PyTypeObject MethodDescriptorNopGet_Type = {
335     PyVarObject_HEAD_INIT(NULL, 0)
336     "MethodDescriptorNopGet",
337     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
338     .tp_call = call_return_args,
339     .tp_descr_get = nop_descr_get,
340 };
341 
342 typedef struct {
343     MethodDescriptorObject base;
344     vectorcallfunc vectorcall;
345 } MethodDescriptor2Object;
346 
347 static PyObject *
MethodDescriptor2_new(PyTypeObject * type,PyObject * args,PyObject * kw)348 MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw)
349 {
350     MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type);
351     if (op == NULL) {
352         return NULL;
353     }
354     op->base.vectorcall = NULL;
355     op->vectorcall = MethodDescriptor_vectorcall;
356     return (PyObject *)op;
357 }
358 
359 static PyTypeObject MethodDescriptor2_Type = {
360     PyVarObject_HEAD_INIT(NULL, 0)
361     "MethodDescriptor2",
362     sizeof(MethodDescriptor2Object),
363     .tp_new = MethodDescriptor2_new,
364     .tp_call = PyVectorcall_Call,
365     .tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall),
366     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL,
367 };
368 
369 
370 int
_PyTestCapi_Init_Vectorcall(PyObject * m)371 _PyTestCapi_Init_Vectorcall(PyObject *m) {
372     if (PyModule_AddFunctions(m, TestMethods) < 0) {
373         return -1;
374     }
375 
376     if (PyType_Ready(&MethodDescriptorBase_Type) < 0) {
377         return -1;
378     }
379     if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) {
380         return -1;
381     }
382 
383     MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type;
384     if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) {
385         return -1;
386     }
387     if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) {
388         return -1;
389     }
390 
391     MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type;
392     if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) {
393         return -1;
394     }
395     if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) {
396         return -1;
397     }
398 
399     MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type;
400     if (PyType_Ready(&MethodDescriptor2_Type) < 0) {
401         return -1;
402     }
403     if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) {
404         return -1;
405     }
406 
407     return 0;
408 }
409