LCOV - code coverage report
Current view: top level - Modules/_testcapi - vectorcall.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 65 101 64.4 %
Date: 2022-07-20 13:12:14 Functions: 10 12 83.3 %
Branches: 30 56 53.6 %

           Branch data     Line data    Source code
       1                 :            : #include "parts.h"
       2                 :            : #include <stddef.h>  // offsetof
       3                 :            : 
       4                 :            : 
       5                 :            : /* Test PEP 590 - Vectorcall */
       6                 :            : 
       7                 :            : static int
       8                 :        368 : fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
       9                 :            : {
      10         [ +  + ]:        368 :     if (args == Py_None) {
      11                 :         84 :         *stack = NULL;
      12                 :         84 :         *nargs = 0;
      13                 :            :     }
      14         [ +  - ]:        284 :     else if (PyTuple_Check(args)) {
      15                 :        284 :         *stack = ((PyTupleObject *)args)->ob_item;
      16                 :        284 :         *nargs = PyTuple_GET_SIZE(args);
      17                 :            :     }
      18                 :            :     else {
      19                 :          0 :         PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
      20                 :          0 :         return -1;
      21                 :            :     }
      22                 :        368 :     return 0;
      23                 :            : }
      24                 :            : 
      25                 :            : 
      26                 :            : static PyObject *
      27                 :         69 : test_pyobject_fastcall(PyObject *self, PyObject *args)
      28                 :            : {
      29                 :            :     PyObject *func, *func_args;
      30                 :            :     PyObject **stack;
      31                 :            :     Py_ssize_t nargs;
      32                 :            : 
      33         [ -  + ]:         69 :     if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) {
      34                 :          0 :         return NULL;
      35                 :            :     }
      36                 :            : 
      37         [ -  + ]:         69 :     if (fastcall_args(func_args, &stack, &nargs) < 0) {
      38                 :          0 :         return NULL;
      39                 :            :     }
      40                 :         69 :     return _PyObject_FastCall(func, stack, nargs);
      41                 :            : }
      42                 :            : 
      43                 :            : static PyObject *
      44                 :        103 : test_pyobject_fastcalldict(PyObject *self, PyObject *args)
      45                 :            : {
      46                 :            :     PyObject *func, *func_args, *kwargs;
      47                 :            :     PyObject **stack;
      48                 :            :     Py_ssize_t nargs;
      49                 :            : 
      50         [ -  + ]:        103 :     if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) {
      51                 :          0 :         return NULL;
      52                 :            :     }
      53                 :            : 
      54         [ -  + ]:        103 :     if (fastcall_args(func_args, &stack, &nargs) < 0) {
      55                 :          0 :         return NULL;
      56                 :            :     }
      57                 :            : 
      58         [ +  + ]:        103 :     if (kwargs == Py_None) {
      59                 :         69 :         kwargs = NULL;
      60                 :            :     }
      61         [ -  + ]:         34 :     else if (!PyDict_Check(kwargs)) {
      62                 :          0 :         PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
      63                 :          0 :         return NULL;
      64                 :            :     }
      65                 :            : 
      66                 :        103 :     return PyObject_VectorcallDict(func, stack, nargs, kwargs);
      67                 :            : }
      68                 :            : 
      69                 :            : static PyObject *
      70                 :        196 : test_pyobject_vectorcall(PyObject *self, PyObject *args)
      71                 :            : {
      72                 :        196 :     PyObject *func, *func_args, *kwnames = NULL;
      73                 :            :     PyObject **stack;
      74                 :            :     Py_ssize_t nargs, nkw;
      75                 :            : 
      76         [ -  + ]:        196 :     if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) {
      77                 :          0 :         return NULL;
      78                 :            :     }
      79                 :            : 
      80         [ -  + ]:        196 :     if (fastcall_args(func_args, &stack, &nargs) < 0) {
      81                 :          0 :         return NULL;
      82                 :            :     }
      83                 :            : 
      84         [ +  + ]:        196 :     if (kwnames == Py_None) {
      85                 :         79 :         kwnames = NULL;
      86                 :            :     }
      87         [ +  - ]:        117 :     else if (PyTuple_Check(kwnames)) {
      88                 :        117 :         nkw = PyTuple_GET_SIZE(kwnames);
      89         [ -  + ]:        117 :         if (nargs < nkw) {
      90                 :          0 :             PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
      91                 :          0 :             return NULL;
      92                 :            :         }
      93                 :        117 :         nargs -= nkw;
      94                 :            :     }
      95                 :            :     else {
      96                 :          0 :         PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
      97                 :          0 :         return NULL;
      98                 :            :     }
      99                 :        196 :     return PyObject_Vectorcall(func, stack, nargs, kwnames);
     100                 :            : }
     101                 :            : 
     102                 :            : static PyObject *
     103                 :         16 : test_pyvectorcall_call(PyObject *self, PyObject *args)
     104                 :            : {
     105                 :            :     PyObject *func;
     106                 :            :     PyObject *argstuple;
     107                 :         16 :     PyObject *kwargs = NULL;
     108                 :            : 
     109         [ -  + ]:         16 :     if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
     110                 :          0 :         return NULL;
     111                 :            :     }
     112                 :            : 
     113         [ -  + ]:         16 :     if (!PyTuple_Check(argstuple)) {
     114                 :          0 :         PyErr_SetString(PyExc_TypeError, "args must be a tuple");
     115                 :          0 :         return NULL;
     116                 :            :     }
     117   [ +  +  -  + ]:         16 :     if (kwargs != NULL && !PyDict_Check(kwargs)) {
     118                 :          0 :         PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
     119                 :          0 :         return NULL;
     120                 :            :     }
     121                 :            : 
     122                 :         16 :     return PyVectorcall_Call(func, argstuple, kwargs);
     123                 :            : }
     124                 :            : 
     125                 :            : static PyMethodDef TestMethods[] = {
     126                 :            :     {"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
     127                 :            :     {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
     128                 :            :     {"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
     129                 :            :     {"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
     130                 :            :     {NULL},
     131                 :            : };
     132                 :            : 
     133                 :            : 
     134                 :            : typedef struct {
     135                 :            :     PyObject_HEAD
     136                 :            :     vectorcallfunc vectorcall;
     137                 :            : } MethodDescriptorObject;
     138                 :            : 
     139                 :            : static PyObject *
     140                 :         46 : MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args,
     141                 :            :                             size_t nargsf, PyObject *kwnames)
     142                 :            : {
     143                 :            :     /* True if using the vectorcall function in MethodDescriptorObject
     144                 :            :      * but False for MethodDescriptor2Object */
     145                 :         46 :     MethodDescriptorObject *md = (MethodDescriptorObject *)callable;
     146                 :         46 :     return PyBool_FromLong(md->vectorcall != NULL);
     147                 :            : }
     148                 :            : 
     149                 :            : static PyObject *
     150                 :          6 : MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw)
     151                 :            : {
     152                 :          6 :     MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0);
     153                 :          6 :     op->vectorcall = MethodDescriptor_vectorcall;
     154                 :          6 :     return (PyObject *)op;
     155                 :            : }
     156                 :            : 
     157                 :            : static PyObject *
     158                 :          0 : func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
     159                 :            : {
     160   [ #  #  #  # ]:          0 :     if (obj == Py_None || obj == NULL) {
     161                 :          0 :         Py_INCREF(func);
     162                 :          0 :         return func;
     163                 :            :     }
     164                 :          0 :     return PyMethod_New(func, obj);
     165                 :            : }
     166                 :            : 
     167                 :            : static PyObject *
     168                 :          0 : nop_descr_get(PyObject *func, PyObject *obj, PyObject *type)
     169                 :            : {
     170                 :          0 :     Py_INCREF(func);
     171                 :          0 :     return func;
     172                 :            : }
     173                 :            : 
     174                 :            : static PyObject *
     175                 :          1 : call_return_args(PyObject *self, PyObject *args, PyObject *kwargs)
     176                 :            : {
     177                 :          1 :     Py_INCREF(args);
     178                 :          1 :     return args;
     179                 :            : }
     180                 :            : 
     181                 :            : static PyTypeObject MethodDescriptorBase_Type = {
     182                 :            :     PyVarObject_HEAD_INIT(NULL, 0)
     183                 :            :     "MethodDescriptorBase",
     184                 :            :     sizeof(MethodDescriptorObject),
     185                 :            :     .tp_new = MethodDescriptor_new,
     186                 :            :     .tp_call = PyVectorcall_Call,
     187                 :            :     .tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall),
     188                 :            :     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
     189                 :            :                 Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL,
     190                 :            :     .tp_descr_get = func_descr_get,
     191                 :            : };
     192                 :            : 
     193                 :            : static PyTypeObject MethodDescriptorDerived_Type = {
     194                 :            :     PyVarObject_HEAD_INIT(NULL, 0)
     195                 :            :     "MethodDescriptorDerived",
     196                 :            :     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     197                 :            : };
     198                 :            : 
     199                 :            : static PyTypeObject MethodDescriptorNopGet_Type = {
     200                 :            :     PyVarObject_HEAD_INIT(NULL, 0)
     201                 :            :     "MethodDescriptorNopGet",
     202                 :            :     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     203                 :            :     .tp_call = call_return_args,
     204                 :            :     .tp_descr_get = nop_descr_get,
     205                 :            : };
     206                 :            : 
     207                 :            : typedef struct {
     208                 :            :     MethodDescriptorObject base;
     209                 :            :     vectorcallfunc vectorcall;
     210                 :            : } MethodDescriptor2Object;
     211                 :            : 
     212                 :            : static PyObject *
     213                 :          1 : MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw)
     214                 :            : {
     215                 :          1 :     MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type);
     216                 :          1 :     op->base.vectorcall = NULL;
     217                 :          1 :     op->vectorcall = MethodDescriptor_vectorcall;
     218                 :          1 :     return (PyObject *)op;
     219                 :            : }
     220                 :            : 
     221                 :            : static PyTypeObject MethodDescriptor2_Type = {
     222                 :            :     PyVarObject_HEAD_INIT(NULL, 0)
     223                 :            :     "MethodDescriptor2",
     224                 :            :     sizeof(MethodDescriptor2Object),
     225                 :            :     .tp_new = MethodDescriptor2_new,
     226                 :            :     .tp_call = PyVectorcall_Call,
     227                 :            :     .tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall),
     228                 :            :     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL,
     229                 :            : };
     230                 :            : 
     231                 :            : 
     232                 :            : int
     233                 :       1170 : _PyTestCapi_Init_Vectorcall(PyObject *m) {
     234         [ -  + ]:       1170 :     if (PyModule_AddFunctions(m, TestMethods) < 0) {
     235                 :          0 :         return -1;
     236                 :            :     }
     237                 :            : 
     238         [ -  + ]:       1170 :     if (PyType_Ready(&MethodDescriptorBase_Type) < 0) {
     239                 :          0 :         return -1;
     240                 :            :     }
     241         [ -  + ]:       1170 :     if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) {
     242                 :          0 :         return -1;
     243                 :            :     }
     244                 :            : 
     245                 :       1170 :     MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type;
     246         [ -  + ]:       1170 :     if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) {
     247                 :          0 :         return -1;
     248                 :            :     }
     249         [ -  + ]:       1170 :     if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) {
     250                 :          0 :         return -1;
     251                 :            :     }
     252                 :            : 
     253                 :       1170 :     MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type;
     254         [ -  + ]:       1170 :     if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) {
     255                 :          0 :         return -1;
     256                 :            :     }
     257         [ -  + ]:       1170 :     if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) {
     258                 :          0 :         return -1;
     259                 :            :     }
     260                 :            : 
     261                 :       1170 :     MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type;
     262         [ -  + ]:       1170 :     if (PyType_Ready(&MethodDescriptor2_Type) < 0) {
     263                 :          0 :         return -1;
     264                 :            :     }
     265         [ -  + ]:       1170 :     if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) {
     266                 :          0 :         return -1;
     267                 :            :     }
     268                 :            : 
     269                 :       1170 :     return 0;
     270                 :            : }

Generated by: LCOV version 1.14