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