(root)/
Python-3.12.0/
Modules/
_testcapi/
vectorcall.c
       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  }