LCOV - code coverage report
Current view: | top level - Modules/_testcapi - vectorcall.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 14 | 126 | 11.1 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 1 | 19 | 5.3 %
|  |  | Branches: | 9 | 64 | 14.1 %

               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                 :            : }

Generated by: LCOV version 1.14