diff --git a/Include/abstract.h b/Include/abstract.h index 961279d..b038870 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -177,25 +177,6 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict( PyObject **values, PyObject *kwnames); -/* Convert (args, nargs, kwargs: dict) into a (stack, nargs, kwnames: tuple). - - Return 0 on success, raise an exception and return -1 on error. - - Write the new stack into *p_stack. If *p_stack is differen than args, it - must be released by PyMem_Free(). - - The stack uses borrowed references. - - The type of keyword keys is not checked, these checks should be done - later (ex: _PyArg_ParseStackAndKeywords). */ -PyAPI_FUNC(int) _PyStack_UnpackDict( - PyObject **args, - Py_ssize_t nargs, - PyObject *kwargs, - PyObject ***p_stack, - PyObject **p_kwnames, - PyObject *func); - /* Suggested size (number of positional arguments) for arrays of PyObject* allocated on a C stack to avoid allocating memory on the heap memory. Such array is used to pass positional arguments to call functions of the @@ -255,12 +236,28 @@ PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend( PyObject *args, PyObject *kwargs); +PyAPI_FUNC(PyObject *) _PyObject_FastCall_Prepend(PyObject *func, + PyObject *obj, + PyObject **args, + Py_ssize_t nargs, + PyObject *kwnames); + PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where); #endif /* Py_LIMITED_API */ +#ifdef Py_BUILD_CORE +PyAPI_FUNC(PyObject *) _Py_FastCall_FromArgs( + PyObject *self, + fastternaryfunc fastcall, + PyObject **args, + Py_ssize_t nargs, + PyObject *kwargs); +#endif + + /* Call a callable Python object 'callable', with arguments given by the tuple 'args'. If no arguments are needed, then 'args' can be *NULL*. diff --git a/Include/methodobject.h b/Include/methodobject.h index 79fad82..1ad4c8c 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -16,8 +16,6 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type; #define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type) typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); -typedef PyObject *(*_PyCFunctionFast) (PyObject *self, PyObject **args, - Py_ssize_t nargs, PyObject *kwnames); typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*PyNoArgsFunction)(PyObject *); diff --git a/Include/object.h b/Include/object.h index 63e37b8..0587dca 100644 --- a/Include/object.h +++ b/Include/object.h @@ -339,6 +339,8 @@ typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *); typedef int (*initproc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *); typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t); +typedef PyObject* (*fastternaryfunc) (PyObject *self, PyObject **stack, + Py_ssize_t nargs, PyObject *kwnames); #ifdef Py_LIMITED_API typedef struct _typeobject PyTypeObject; /* opaque */ @@ -424,6 +426,8 @@ typedef struct _typeobject { destructor tp_finalize; + fastternaryfunc tp_fastcall; + #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Py_ssize_t tp_allocs; @@ -631,12 +635,11 @@ given type object has a specified feature. /* Objects support garbage collection (see objimp.h) */ #define Py_TPFLAGS_HAVE_GC (1UL << 14) -/* These two bits are preserved for Stackless Python, next after this is 17 */ +/* Bits 15 and 16 were used by Stackless Python, but are now used for tp_fastcall */ #ifdef STACKLESS -#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION (3UL << 15) -#else -#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0 +# error "Stackless is no more supported" #endif +#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0 /* Objects support type attribute cache */ #define Py_TPFLAGS_HAVE_VERSION_TAG (1UL << 18) @@ -666,6 +669,9 @@ given type object has a specified feature. /* Type structure has tp_finalize member (3.4) */ #define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0) +/* Type structure has tp_fastcall member (3.7) */ +#define Py_TPFLAGS_HAVE_FASTCALL (1UL << 15) + #ifdef Py_LIMITED_API #define PyType_HasFeature(t,f) ((PyType_GetFlags(t) & (f)) != 0) #else @@ -674,6 +680,13 @@ given type object has a specified feature. #define PyType_FastSubclass(t,f) PyType_HasFeature(t,f) +#ifndef Py_LIMITED_API +# define _Py_TYPE_HAS_FASTCALL(type) \ + (((type)->tp_flags & Py_TPFLAGS_HAVE_FASTCALL) \ + && ((type)->tp_fastcall != NULL)) +#endif + + /* The macros Py_INCREF(op) and Py_DECREF(op) are used to increment or decrement reference counts. Py_DECREF calls the object's deallocator function when diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index e6d8e50..1074658 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1092,7 +1092,7 @@ class SizeofTest(unittest.TestCase): check((1,2,3), vsize('') + 3*self.P) # type # static type: PyTypeObject - fmt = 'P2n15Pl4Pn9Pn11PIP' + fmt = 'P2n15Pl4Pn9Pn11PIPP' if hasattr(sys, 'getcounts'): fmt += '3n2P' s = vsize(fmt) diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 96be0f4..24456a6 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -375,7 +375,6 @@ read_bytes(bytesio *self, Py_ssize_t size) /*[clinic input] _io.BytesIO.read size as arg: object = None - / Read at most size bytes, returned as a bytes object. @@ -385,7 +384,7 @@ Return an empty bytes object at EOF. static PyObject * _io_BytesIO_read_impl(bytesio *self, PyObject *arg) -/*[clinic end generated code: output=85dacb535c1e1781 input=cc7ba4a797bb1555]*/ +/*[clinic end generated code: output=85dacb535c1e1781 input=658872d314d03450]*/ { Py_ssize_t size, n; diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h index 656e7ec..9dec356 100644 --- a/Modules/_io/clinic/bytesio.c.h +++ b/Modules/_io/clinic/bytesio.c.h @@ -149,7 +149,7 @@ _io_BytesIO_tell(bytesio *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_io_BytesIO_read__doc__, -"read($self, size=None, /)\n" +"read($self, /, size=None)\n" "--\n" "\n" "Read at most size bytes, returned as a bytes object.\n" @@ -167,17 +167,14 @@ static PyObject * _io_BytesIO_read(bytesio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + static const char * const _keywords[] = {"size", NULL}; + static _PyArg_Parser _parser = {"|O:read", _keywords, 0}; PyObject *arg = Py_None; - if (!_PyArg_UnpackStack(args, nargs, "read", - 0, 1, + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, &arg)) { goto exit; } - - if (!_PyArg_NoStackKeywords("read", kwnames)) { - goto exit; - } return_value = _io_BytesIO_read_impl(self, arg); exit: @@ -472,4 +469,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=138ee6ad6951bc84 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6390aa7ce857ac6e input=a9049054013a1b77]*/ diff --git a/Modules/_operator.c b/Modules/_operator.c index f5b8612..faae56f 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -455,24 +455,29 @@ itemgetter_traverse(itemgetterobject *ig, visitproc visit, void *arg) } static PyObject * -itemgetter_call(itemgetterobject *ig, PyObject *args, PyObject *kw) +itemgetter_call(itemgetterobject *ig, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) { PyObject *obj, *result; Py_ssize_t i, nitems=ig->nitems; - if (kw != NULL && !_PyArg_NoKeywords("itemgetter", kw)) + if (kwnames != NULL && !_PyArg_NoStackKeywords("itemgetter", kwnames)) { return NULL; - if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &obj)) + } + if (!_PyArg_UnpackStack(args, nargs, "itemgetter", 1, 1, &obj)) { return NULL; - if (nitems == 1) + } + if (nitems == 1) { return PyObject_GetItem(obj, ig->item); + } assert(PyTuple_Check(ig->item)); assert(PyTuple_GET_SIZE(ig->item) == nitems); result = PyTuple_New(nitems); - if (result == NULL) + if (result == NULL) { return NULL; + } for (i=0 ; i < nitems ; i++) { PyObject *item, *val; @@ -545,14 +550,15 @@ static PyTypeObject itemgetter_type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)itemgetter_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ itemgetter_doc, /* tp_doc */ - (traverseproc)itemgetter_traverse, /* tp_traverse */ + (traverseproc)itemgetter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -570,6 +576,16 @@ static PyTypeObject itemgetter_type = { 0, /* tp_alloc */ itemgetter_new, /* tp_new */ 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)itemgetter_call, /* tp_fastcall */ }; @@ -744,24 +760,30 @@ dotted_getattr(PyObject *obj, PyObject *attr) } static PyObject * -attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw) +attrgetter_call(attrgetterobject *ag, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) { PyObject *obj, *result; Py_ssize_t i, nattrs=ag->nattrs; - if (kw != NULL && !_PyArg_NoKeywords("attrgetter", kw)) + if (kwnames != NULL && !_PyArg_NoStackKeywords("attrgetter", kwnames)) { return NULL; - if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &obj)) + } + if (!_PyArg_UnpackStack(args, nargs, "attrgetter", 1, 1, &obj)) { return NULL; - if (ag->nattrs == 1) /* ag->attr is always a tuple */ + } + /* ag->attr is always a tuple */ + if (ag->nattrs == 1) { return dotted_getattr(obj, PyTuple_GET_ITEM(ag->attr, 0)); + } assert(PyTuple_Check(ag->attr)); assert(PyTuple_GET_SIZE(ag->attr) == nattrs); result = PyTuple_New(nattrs); - if (result == NULL) + if (result == NULL) { return NULL; + } for (i=0 ; i < nattrs ; i++) { PyObject *attr, *val; @@ -776,6 +798,7 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw) return result; } + static PyObject * dotjoinattr(PyObject *attr, PyObject **attrsep) { @@ -888,14 +911,15 @@ static PyTypeObject attrgetter_type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)attrgetter_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ attrgetter_doc, /* tp_doc */ - (traverseproc)attrgetter_traverse, /* tp_traverse */ + (traverseproc)attrgetter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -913,6 +937,16 @@ static PyTypeObject attrgetter_type = { 0, /* tp_alloc */ attrgetter_new, /* tp_new */ 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)attrgetter_call, /* tp_fastcall */ }; @@ -988,13 +1022,14 @@ methodcaller_traverse(methodcallerobject *mc, visitproc visit, void *arg) } static PyObject * -methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw) +methodcaller_call(methodcallerobject *mc, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) { PyObject *method, *obj, *result; - if (kw != NULL && !_PyArg_NoKeywords("methodcaller", kw)) + if (kwnames != NULL && !_PyArg_NoStackKeywords("methodcaller", kwnames)) return NULL; - if (!PyArg_UnpackTuple(args, "methodcaller", 1, 1, &obj)) + if (!_PyArg_UnpackStack(args, nargs, "methodcaller", 1, 1, &obj)) return NULL; method = PyObject_GetAttr(obj, mc->name); if (method == NULL) @@ -1151,14 +1186,15 @@ static PyTypeObject methodcaller_type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)methodcaller_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ methodcaller_doc, /* tp_doc */ - (traverseproc)methodcaller_traverse, /* tp_traverse */ + (traverseproc)methodcaller_traverse,/* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -1176,6 +1212,16 @@ static PyTypeObject methodcaller_type = { 0, /* tp_alloc */ methodcaller_new, /* tp_new */ 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)methodcaller_call, /* tp_fastcall */ }; diff --git a/Objects/abstract.c b/Objects/abstract.c index ee50f02..29253b0 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2328,11 +2328,17 @@ _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, else if (PyCFunction_Check(callable)) { result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs); } + /* FIXME: should we avoid tp_fastcall if tp_call is not fastcall_wrapper? */ + else if (_Py_TYPE_HAS_FASTCALL(Py_TYPE(callable))) { + fastternaryfunc fastcall = Py_TYPE(callable)->tp_fastcall; + + result = _Py_FastCall_FromArgs(callable, fastcall, args, nargs, kwargs); + } else { PyObject *tuple; /* Slow-path: build a temporary tuple */ - call = callable->ob_type->tp_call; + call = Py_TYPE(callable)->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", callable->ob_type->tp_name); @@ -2364,17 +2370,17 @@ _PyObject_Call_Prepend(PyObject *callable, { PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; PyObject **stack; - Py_ssize_t argcount; + Py_ssize_t nargs; PyObject *result; assert(PyTuple_Check(args)); - argcount = PyTuple_GET_SIZE(args); - if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + nargs = PyTuple_GET_SIZE(args); + if (nargs + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { stack = small_stack; } else { - stack = PyMem_Malloc((argcount + 1) * sizeof(PyObject *)); + stack = PyMem_Malloc((nargs + 1) * sizeof(PyObject *)); if (stack == NULL) { PyErr_NoMemory(); return NULL; @@ -2384,11 +2390,11 @@ _PyObject_Call_Prepend(PyObject *callable, /* use borrowed references */ stack[0] = obj; memcpy(&stack[1], - &PyTuple_GET_ITEM(args, 0), - argcount * sizeof(PyObject *)); + &PyTuple_GET_ITEM(args, 0), + nargs * sizeof(PyObject *)); result = _PyObject_FastCallDict(callable, - stack, argcount + 1, + stack, nargs + 1, kwargs); if (stack != small_stack) { PyMem_Free(stack); @@ -2397,6 +2403,46 @@ _PyObject_Call_Prepend(PyObject *callable, } PyObject * +_PyObject_FastCall_Prepend(PyObject *func, PyObject *obj, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject **stack; + PyObject *result; + Py_ssize_t alloc; + + alloc = nargs + 1; + if (kwnames != NULL) { + alloc += PyTuple_GET_SIZE(kwnames); + } + + if (alloc <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; + } + else { + stack = PyMem_Malloc(alloc * sizeof(PyObject *)); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + + /* use borrowed references */ + stack[0] = obj; + memcpy(&stack[1], args, (alloc - 1) * sizeof(PyObject *)); + + result = _PyObject_FastCallKeywords(func, + stack, nargs + 1, + kwnames); + if (stack != small_stack) { + PyMem_Free(stack); + } + + result = _Py_CheckFunctionResult(func, result, NULL); + return result; +} + +PyObject * _PyStack_AsDict(PyObject **values, PyObject *kwnames) { Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); @@ -2421,9 +2467,9 @@ _PyStack_AsDict(PyObject **values, PyObject *kwnames) return kwdict; } -int +static int _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, - PyObject ***p_stack, PyObject **p_kwnames, PyObject *func) + PyObject ***p_stack, PyObject **p_kwnames) { PyObject **stack, **kwstack; Py_ssize_t nkwargs; @@ -2435,6 +2481,7 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, assert(kwargs == NULL || PyDict_CheckExact(kwargs)); if (kwargs == NULL || (nkwargs = PyDict_GET_SIZE(kwargs)) == 0) { + /* Note: args can be NULL */ *p_stack = args; *p_kwnames = NULL; return 0; @@ -2457,8 +2504,11 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, return -1; } - /* Copy position arguments (borrowed references) */ - memcpy(stack, args, nargs * sizeof(stack[0])); + /* args is NULL if nargs==0: don't call memcpy(stack, NULL, 0) */ + if (nargs) { + /* Copy position arguments (borrowed references) */ + memcpy(stack, args, nargs * sizeof(stack[0])); + } kwstack = stack + nargs; pos = i = 0; @@ -2479,6 +2529,30 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, } PyObject * +_Py_FastCall_FromArgs(PyObject *self, fastternaryfunc fastcall, + PyObject **args, Py_ssize_t nargs, PyObject *kwargs) +{ + PyObject **stack; + PyObject *kwnames; + PyObject *result; + + assert(!PyErr_Occurred()); + + if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) { + return NULL; + } + + result = fastcall(self, stack, nargs, kwnames); + if (stack != args) { + PyMem_Free(stack); + } + Py_XDECREF(kwnames); + + result = _Py_CheckFunctionResult(self, result, NULL); + return result; +} + +PyObject * _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t nargs, PyObject *kwnames) { @@ -2490,11 +2564,27 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg _PyArg_ParseStackAndKeywords(). */ if (PyFunction_Check(callable)) { + /* FIXME: should we use Py_EnterRecursiveCall() here? */ return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames); } if (PyCFunction_Check(callable)) { return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames); } + else if (_Py_TYPE_HAS_FASTCALL(Py_TYPE(callable))) { + fastternaryfunc fastcall = Py_TYPE(callable)->tp_fastcall; + PyObject *result; + + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + + result = fastcall(callable, stack, nargs, kwnames); + result = _Py_CheckFunctionResult(callable, result, NULL); + + Py_LeaveRecursiveCall(); + + return result; + } else { /* Slow-path: build a temporary tuple for positional arguments and a temporary dictionary for keyword arguments (if any) */ @@ -2512,7 +2602,7 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg return NULL; } - call = callable->ob_type->tp_call; + call = Py_TYPE(callable)->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", callable->ob_type->tp_name); @@ -2539,6 +2629,8 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg Py_DECREF(argtuple); Py_XDECREF(kwdict); + result = _Py_CheckFunctionResult(callable, result, NULL); + exit: Py_LeaveRecursiveCall(); return result; diff --git a/Objects/classobject.c b/Objects/classobject.c index b0ed023..1fc821c 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -302,7 +302,8 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg) } static PyObject * -method_call(PyObject *method, PyObject *args, PyObject *kwargs) +method_call(PyObject *method, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) { PyObject *self, *func; @@ -313,8 +314,7 @@ method_call(PyObject *method, PyObject *args, PyObject *kwargs) } func = PyMethod_GET_FUNCTION(method); - - return _PyObject_Call_Prepend(func, self, args, kwargs); + return _PyObject_FastCall_Prepend(func, self, args, nargs, kwnames); } static PyObject * @@ -346,12 +346,13 @@ PyTypeObject PyMethod_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)method_hash, /* tp_hash */ - method_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ method_getattro, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ method_doc, /* tp_doc */ (traverseproc)method_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -370,6 +371,17 @@ PyTypeObject PyMethod_Type = { 0, /* tp_init */ 0, /* tp_alloc */ method_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)method_call, /* tp_fastcall */ }; /* Clear out the free list */ diff --git a/Objects/descrobject.c b/Objects/descrobject.c index ed39891..1d17768 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -210,15 +210,13 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) } static PyObject * -methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) +methoddescr_call(PyMethodDescrObject *descr, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) { - Py_ssize_t argc; - PyObject *self, *func, *result, **stack; + PyObject *self, *func, *result; /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - argc = PyTuple_GET_SIZE(args); - if (argc < 1) { + if (nargs < 1) { PyErr_Format(PyExc_TypeError, "descriptor '%V' of '%.100s' " "object needs an argument", @@ -226,7 +224,7 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) PyDescr_TYPE(descr)->tp_name); return NULL; } - self = PyTuple_GET_ITEM(args, 0); + self = args[0]; if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), (PyObject *)PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, @@ -242,23 +240,19 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) func = PyCFunction_NewEx(descr->d_method, self, NULL); if (func == NULL) return NULL; - stack = &PyTuple_GET_ITEM(args, 1); - result = _PyObject_FastCallDict(func, stack, argc - 1, kwds); + result = _PyCFunction_FastCallKeywords(func, args + 1, nargs - 1, kwnames); Py_DECREF(func); return result; } static PyObject * -classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, - PyObject *kwds) +classmethoddescr_call(PyMethodDescrObject *descr, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) { - Py_ssize_t argc; - PyObject *self, *func, *result, **stack; + PyObject *self, *func, *result; /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - argc = PyTuple_GET_SIZE(args); - if (argc < 1) { + if (nargs < 1) { PyErr_Format(PyExc_TypeError, "descriptor '%V' of '%.100s' " "object needs an argument", @@ -266,7 +260,7 @@ classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyDescr_TYPE(descr)->tp_name); return NULL; } - self = PyTuple_GET_ITEM(args, 0); + self = args[0]; if (!PyType_Check(self)) { PyErr_Format(PyExc_TypeError, "descriptor '%V' requires a type " @@ -290,22 +284,19 @@ classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, func = PyCFunction_NewEx(descr->d_method, self, NULL); if (func == NULL) return NULL; - stack = &PyTuple_GET_ITEM(args, 1); - result = _PyObject_FastCallDict(func, stack, argc - 1, kwds); + result = _PyObject_FastCallKeywords(func, args + 1, nargs - 1, kwnames); Py_DECREF(func); return result; } static PyObject * -wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) +wrapperdescr_call(PyMethodDescrObject *descr, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) { - Py_ssize_t argc; - PyObject *self, *func, *result, **stack; + PyObject *self, *func, *result; /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - argc = PyTuple_GET_SIZE(args); - if (argc < 1) { + if (nargs < 1) { PyErr_Format(PyExc_TypeError, "descriptor '%V' of '%.100s' " "object needs an argument", @@ -313,7 +304,7 @@ wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) PyDescr_TYPE(descr)->tp_name); return NULL; } - self = PyTuple_GET_ITEM(args, 0); + self = args[0]; if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), (PyObject *)PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, @@ -330,8 +321,7 @@ wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) if (func == NULL) return NULL; - stack = &PyTuple_GET_ITEM(args, 1); - result = _PyObject_FastCallDict(func, stack, argc - 1, kwds); + result = _PyObject_FastCallKeywords(func, args + 1, nargs - 1, kwnames); Py_DECREF(func); return result; } @@ -491,12 +481,13 @@ PyTypeObject PyMethodDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)methoddescr_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ 0, /* tp_doc */ descr_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -511,6 +502,21 @@ PyTypeObject PyMethodDescr_Type = { 0, /* tp_dict */ (descrgetfunc)method_get, /* tp_descr_get */ 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)methoddescr_call, /* tp_fastcall */ }; /* This is for METH_CLASS in C, not for "f = classmethod(f)" in Python! */ @@ -529,12 +535,13 @@ PyTypeObject PyClassMethodDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)classmethoddescr_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ 0, /* tp_doc */ descr_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -549,6 +556,21 @@ PyTypeObject PyClassMethodDescr_Type = { 0, /* tp_dict */ (descrgetfunc)classmethod_get, /* tp_descr_get */ 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)classmethoddescr_call, /* tp_fastcall */ }; PyTypeObject PyMemberDescr_Type = { @@ -640,12 +662,13 @@ PyTypeObject PyWrapperDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)wrapperdescr_call, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ 0, /* tp_doc */ descr_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -660,6 +683,21 @@ PyTypeObject PyWrapperDescr_Type = { 0, /* tp_dict */ (descrgetfunc)wrapperdescr_get, /* tp_descr_get */ 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)wrapperdescr_call, /* tp_fastcall */ }; static PyDescrObject * @@ -1359,10 +1397,8 @@ property_dealloc(PyObject *self) static PyObject * property_descr_get(PyObject *self, PyObject *obj, PyObject *type) { - static PyObject * volatile cached_args = NULL; - PyObject *args; - PyObject *ret; propertyobject *gs = (propertyobject *)self; + PyObject *args[1]; if (obj == NULL || obj == Py_None) { Py_INCREF(self); @@ -1372,29 +1408,9 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type) PyErr_SetString(PyExc_AttributeError, "unreadable attribute"); return NULL; } - args = cached_args; - cached_args = NULL; - if (!args) { - args = PyTuple_New(1); - if (!args) - return NULL; - _PyObject_GC_UNTRACK(args); - } - Py_INCREF(obj); - PyTuple_SET_ITEM(args, 0, obj); - ret = PyObject_Call(gs->prop_get, args, NULL); - if (cached_args == NULL && Py_REFCNT(args) == 1) { - assert(Py_SIZE(args) == 1); - assert(PyTuple_GET_ITEM(args, 0) == obj); - cached_args = args; - Py_DECREF(obj); - } - else { - assert(Py_REFCNT(args) >= 1); - _PyObject_GC_TRACK(args); - Py_DECREF(args); - } - return ret; + + args[0] = obj; + return _PyObject_FastCall(gs->prop_get, args, 1); } static int diff --git a/Objects/funcobject.c b/Objects/funcobject.c index a3af4b3..07ba944 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -562,6 +562,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) return 0; } +#if 0 static PyObject * function_call(PyObject *func, PyObject *args, PyObject *kwargs) { @@ -572,6 +573,7 @@ function_call(PyObject *func, PyObject *args, PyObject *kwargs) nargs = PyTuple_GET_SIZE(args); return _PyFunction_FastCallDict(func, stack, nargs, kwargs); } +#endif /* Bind a function to an object */ static PyObject * @@ -599,12 +601,18 @@ PyTypeObject PyFunction_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ +#if 0 function_call, /* tp_call */ +#else + /* FIXME: revert this change, 0 is used to test fastcall_wrapper() */ + 0, /* tp_call */ +#endif 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ func_doc, /* tp_doc */ (traverseproc)func_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -623,6 +631,17 @@ PyTypeObject PyFunction_Type = { 0, /* tp_init */ 0, /* tp_alloc */ func_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + _PyFunction_FastCallKeywords, /* tp_fastcall */ }; diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 7bff6a0..74fbf71 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -236,20 +236,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, case METH_FASTCALL: { - PyObject **stack; - PyObject *kwnames; - _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; - - if (_PyStack_UnpackDict(args, nargs, kwargs, - &stack, &kwnames, func_obj) < 0) { - return NULL; - } - - result = (*fastmeth) (self, stack, nargs, kwnames); - if (stack != args) { - PyMem_Free(stack); - } - Py_XDECREF(kwnames); + result = _Py_FastCall_FromArgs(self, (fastternaryfunc)meth, + args, nargs, kwargs); break; } @@ -333,7 +321,7 @@ _PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, case METH_FASTCALL: /* Fast-path: avoid temporary dict to pass keyword arguments */ - result = ((_PyCFunctionFast)meth) (self, args, nargs, kwnames); + result = ((fastternaryfunc)meth) (self, args, nargs, kwnames); break; case METH_VARARGS: @@ -602,7 +590,8 @@ PyTypeObject PyCFunction_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ 0, /* tp_doc */ (traverseproc)meth_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -615,6 +604,23 @@ PyTypeObject PyCFunction_Type = { meth_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + _PyCFunction_FastCallKeywords, /* tp_fastcall */ }; /* Clear out the free list */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index a521177..eea10d3 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2467,7 +2467,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) /* Initialize tp_flags */ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | - Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE; + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE | + Py_TPFLAGS_HAVE_FASTCALL; if (base->tp_flags & Py_TPFLAGS_HAVE_GC) type->tp_flags |= Py_TPFLAGS_HAVE_GC; @@ -4672,6 +4673,17 @@ overrides_hash(PyTypeObject *type) return 0; } +/* tp_call slot which calls tp_fastcall */ +static PyObject * +fastcall_wrapper(PyObject *callable, PyObject *args_tuple, PyObject *kwargs) +{ + return _Py_FastCall_FromArgs(callable, + Py_TYPE(callable)->tp_fastcall, + &PyTuple_GET_ITEM(args_tuple, 0), + PyTuple_GET_SIZE(args_tuple), + kwargs); +} + static void inherit_slots(PyTypeObject *type, PyTypeObject *base) { @@ -4795,7 +4807,18 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) /* tp_reserved is ignored */ COPYSLOT(tp_repr); /* tp_hash see tp_richcompare */ - COPYSLOT(tp_call); + + if (type->tp_call != fastcall_wrapper) { + /* don't inherit tp_fastcall if tp_call is defined but + not tp_fastcall */ + if (!type->tp_call && !type->tp_fastcall) { + /* FIXME: do something with Py_TPFLAGS_HAVE_FASTCALL */ + COPYSLOT(tp_fastcall); + } + + COPYSLOT(tp_call); + } + COPYSLOT(tp_str); { /* Copy comparison-related slots only when @@ -4924,6 +4947,14 @@ PyType_Ready(PyTypeObject *type) type->tp_dict = dict; } + if (_Py_TYPE_HAS_FASTCALL(type) && !type->tp_call) { + /* tp_fastcall defined, but not tp_call: tp_call will use a wrapper + calling tp_fastcall. We need to define tp_call before calling + add_operators(), otherwise the __call__ descriptor is not + defined. */ + type->tp_call = fastcall_wrapper; + } + /* Add type-specific descriptors to tp_dict */ if (add_operators(type) < 0) goto error; @@ -6151,6 +6182,22 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds) return res; } +static PyObject * +slot_tp_fastcall(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) +{ + _Py_IDENTIFIER(__call__); + PyObject *meth = lookup_method(self, &PyId___call__); + PyObject *res; + + if (meth == NULL) + return NULL; + + res = _PyObject_FastCallKeywords(meth, args, nargs, kwnames); + + Py_DECREF(meth); + return res; +} + /* There are two slot dispatch functions for tp_getattro. - slot_tp_getattro() is used when __getattribute__ is overridden @@ -6985,6 +7032,13 @@ fixup_slot_dispatchers(PyTypeObject *type) init_slotdefs(); for (p = slotdefs; p->name; ) p = update_one_slot(type, p); + + if (type->tp_call == slot_tp_call && type->tp_fastcall == NULL) { + /* FIXME: is it ok to set the flag here? */ + type->tp_flags |= Py_TPFLAGS_HAVE_FASTCALL; + type->tp_fastcall = slot_tp_fastcall; + type->tp_call = fastcall_wrapper; + } } static void diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index ab6b235..b8bb924 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -461,7 +461,15 @@ proxy_checkref(PyWeakReference *proxy) WRAP_BINARY(proxy_getattr, PyObject_GetAttr) WRAP_UNARY(proxy_str, PyObject_Str) -WRAP_TERNARY(proxy_call, PyEval_CallObjectWithKeywords) + +static PyObject * +proxy_call(PyObject *proxy, PyObject **args, Py_ssize_t nargs, + PyObject *kwnames) +{ + UNWRAP(proxy); + return _PyObject_FastCallKeywords(proxy, args, nargs, kwnames); +} + static PyObject * proxy_repr(PyWeakReference *proxy) @@ -711,12 +719,13 @@ _PyWeakref_CallableProxyType = { &proxy_as_sequence, /* tp_as_sequence */ &proxy_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ - proxy_call, /* tp_call */ + 0, /* tp_call */ proxy_str, /* tp_str */ proxy_getattr, /* tp_getattro */ (setattrofunc)proxy_setattr, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_HAVE_FASTCALL, /* tp_flags */ 0, /* tp_doc */ (traverseproc)gc_traverse, /* tp_traverse */ (inquiry)gc_clear, /* tp_clear */ @@ -724,6 +733,28 @@ _PyWeakref_CallableProxyType = { 0, /* tp_weaklistoffset */ (getiterfunc)proxy_iter, /* tp_iter */ (iternextfunc)proxy_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ + (fastternaryfunc)proxy_call, /* tp_fastcall */ };