diff --git a/Include/abstract.h b/Include/abstract.h index 3ca283a..f1999ef 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -171,22 +171,6 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict( PyObject **values, PyObject *kwnames); -/* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames). - - Return a new stack which should be released by PyMem_Free(), or return - args unchanged if kwargs is NULL or an empty dictionary. - - The stack uses borrowed references. - - The type of keyword keys is not checked, these checks should be done - later (ex: _PyArg_ParseStack). */ -PyAPI_FUNC(PyObject **) _PyStack_UnpackDict( - PyObject **args, - Py_ssize_t nargs, - PyObject *kwargs, - PyObject **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 @@ -246,12 +230,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/modsupport.h b/Include/modsupport.h index 8306ae7..611be0d 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -44,11 +44,15 @@ PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, const char *, char **, ...); PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *); PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...); +PyAPI_FUNC(int) PyArg_UnpackStack(PyObject **args, Py_ssize_t nargs, + const char *name, + Py_ssize_t min, Py_ssize_t max, ...); PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...); PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); #endif #ifndef Py_LIMITED_API PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw); +PyAPI_FUNC(int) _PyArg_NoStackKeywords(const char *funcname, PyObject *kwnames); PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args); PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); diff --git a/Include/object.h b/Include/object.h index 63e37b8..6809a6d 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; 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/_operator.c b/Modules/_operator.c index f5b8612..8e1fa58 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,7 +550,7 @@ 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 */ @@ -570,6 +575,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 +759,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 +797,7 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw) return result; } + static PyObject * dotjoinattr(PyObject *attr, PyObject **attrsep) { @@ -888,7 +910,7 @@ 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 */ @@ -913,6 +935,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 +1020,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,7 +1184,7 @@ 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 */ @@ -1176,6 +1209,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 5726160..cc1fed3 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2305,11 +2305,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(callable)->tp_fastcall) { + 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); @@ -2341,17 +2347,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; @@ -2361,11 +2367,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); @@ -2374,6 +2380,47 @@ _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 *)); + + assert(!PyErr_Occurred()); + 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); @@ -2398,9 +2445,9 @@ _PyStack_AsDict(PyObject **values, PyObject *kwnames) return kwdict; } -PyObject ** +static int _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, - PyObject **p_kwnames, PyObject *func) + PyObject ***p_stack, PyObject **p_kwnames) { PyObject **stack, **kwstack; Py_ssize_t nkwargs; @@ -2412,29 +2459,34 @@ _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 args; + return 0; } if ((size_t)nargs > PY_SSIZE_T_MAX / sizeof(stack[0]) - (size_t)nkwargs) { PyErr_NoMemory(); - return NULL; + return -1; } stack = PyMem_Malloc((nargs + nkwargs) * sizeof(stack[0])); if (stack == NULL) { PyErr_NoMemory(); - return NULL; + return -1; } kwnames = PyTuple_New(nkwargs); if (kwnames == NULL) { PyMem_Free(stack); - return NULL; + 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; @@ -2449,8 +2501,33 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, i++; } + *p_stack = stack; *p_kwnames = kwnames; - return stack; + return 0; +} + +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 * @@ -2465,11 +2542,27 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg _PyArg_ParseStack(). */ 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(callable)->tp_fastcall) { + 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) */ @@ -2487,7 +2580,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); @@ -2514,6 +2607,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..29706c0 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,7 +346,7 @@ 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 */ @@ -370,6 +370,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..4462a81 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,8 +240,7 @@ 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; } @@ -297,15 +294,13 @@ classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, } 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 +308,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 +325,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,7 +485,7 @@ 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 */ @@ -511,6 +505,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! */ @@ -640,7 +649,7 @@ 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 */ @@ -660,6 +669,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 * @@ -1360,7 +1384,6 @@ 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; @@ -1372,27 +1395,37 @@ 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); + + if (Py_TYPE(gs->prop_get)->tp_fastcall) { + PyObject *args[1] = {obj}; + + ret = _PyObject_FastCall(gs->prop_get, args, 1); } else { - assert(Py_REFCNT(args) >= 1); - _PyObject_GC_TRACK(args); - Py_DECREF(args); + PyObject *args; + + 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; } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index a3af4b3..093a5e4 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,7 +601,12 @@ 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 */ @@ -623,6 +630,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 14750b6..b1ed7d6 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -240,20 +240,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, case METH_FASTCALL: { - PyObject **stack; - PyObject *kwnames; - _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; - - stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj); - if (stack == NULL) { - 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; } @@ -520,6 +508,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 05fc7b8..bbc07da 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4671,6 +4671,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) { @@ -4794,7 +4805,17 @@ 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) { + COPYSLOT(tp_fastcall); + } + + COPYSLOT(tp_call); + } + COPYSLOT(tp_str); { /* Copy comparison-related slots only when @@ -4923,6 +4944,14 @@ PyType_Ready(PyTypeObject *type) type->tp_dict = dict; } + if (type->tp_fastcall && !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; diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index ab6b235..8066218 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,7 +719,7 @@ _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 */ @@ -724,6 +732,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 */ }; diff --git a/Python/getargs.c b/Python/getargs.c index 49888d1..fdda271 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2347,21 +2347,16 @@ err: } -int -PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) +static int +PyArg_UnpackStack_impl(PyObject **args, Py_ssize_t l, const char *name, + Py_ssize_t min, Py_ssize_t max, va_list vargs) { - Py_ssize_t i, l; + Py_ssize_t i; PyObject **o; - va_list vargs; assert(min >= 0); assert(min <= max); - if (!PyTuple_Check(args)) { - PyErr_SetString(PyExc_SystemError, - "PyArg_UnpackTuple() argument list is not a tuple"); - return 0; - } - l = PyTuple_GET_SIZE(args); + if (l < min) { if (name != NULL) PyErr_Format( @@ -2376,8 +2371,11 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m (min == max ? "" : "at least "), min, l); return 0; } - if (l == 0) + + if (l == 0) { return 1; + } + if (l > max) { if (name != NULL) PyErr_Format( @@ -2393,17 +2391,54 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m return 0; } + for (i = 0; i < l; i++) { + o = va_arg(vargs, PyObject **); + *o = args[i]; + } + return 1; +} + +int +PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) +{ + PyObject **stack; + Py_ssize_t nargs; + int retval; + va_list vargs; + + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_SystemError, + "PyArg_UnpackTuple() argument list is not a tuple"); + return 0; + } + stack = &PyTuple_GET_ITEM(args, 0); + nargs = PyTuple_GET_SIZE(args); + #ifdef HAVE_STDARG_PROTOTYPES va_start(vargs, max); #else va_start(vargs); #endif - for (i = 0; i < l; i++) { - o = va_arg(vargs, PyObject **); - *o = PyTuple_GET_ITEM(args, i); - } + retval = PyArg_UnpackStack_impl(stack, nargs, name, min, max, vargs); va_end(vargs); - return 1; + return retval; +} + +int +PyArg_UnpackStack(PyObject **args, Py_ssize_t nargs, const char *name, + Py_ssize_t min, Py_ssize_t max, ...) +{ + int retval; + va_list vargs; + +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, max); +#else + va_start(vargs); +#endif + retval = PyArg_UnpackStack_impl(args, nargs, name, min, max, vargs); + va_end(vargs); + return retval; } @@ -2415,8 +2450,9 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m int _PyArg_NoKeywords(const char *funcname, PyObject *kw) { - if (kw == NULL) + if (kw == NULL) { return 1; + } if (!PyDict_CheckExact(kw)) { PyErr_BadInternalCall(); return 0; @@ -2429,6 +2465,25 @@ _PyArg_NoKeywords(const char *funcname, PyObject *kw) return 0; } +int +_PyArg_NoStackKeywords(const char *funcname, PyObject *kwnames) +{ + if (kwnames == NULL) { + return 1; + } + if (!PyTuple_CheckExact(kwnames)) { + PyErr_BadInternalCall(); + return 0; + } + if (PyTuple_GET_SIZE(kwnames) == 0) { + return 1; + } + + PyErr_Format(PyExc_TypeError, "%s does not take keyword arguments", + funcname); + return 0; +} + int _PyArg_NoPositional(const char *funcname, PyObject *args)