diff -r aa7ac93d23b2 Include/descrobject.h --- a/Include/descrobject.h Wed Feb 01 23:12:20 2017 +0200 +++ b/Include/descrobject.h Thu Feb 02 14:09:46 2017 +0900 @@ -87,12 +87,15 @@ struct PyMemberDef; /* forward declaration for following prototype */ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, struct PyMemberDef *); PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API + +PyAPI_FUNC(PyObject *) _PyMethodDescr_FastCallKeywords( + PyObject *descrobj, PyObject **stack, Py_ssize_t nargs, PyObject *kwnames); PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) #endif PyAPI_FUNC(PyObject *) PyDictProxy_New(PyObject *); diff -r aa7ac93d23b2 Include/methodobject.h --- a/Include/methodobject.h Wed Feb 01 23:12:20 2017 +0200 +++ b/Include/methodobject.h Thu Feb 02 14:09:46 2017 +0900 @@ -99,12 +99,19 @@ PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict( PyMethodDef *method, PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwargs); + +PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallKeywords( + PyMethodDef *method, + PyObject *self, + PyObject **args, + Py_ssize_t nargs, + PyObject *kwnames); #endif PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); #ifndef Py_LIMITED_API PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out); diff -r aa7ac93d23b2 Lib/test/test_gdb.py --- a/Lib/test/test_gdb.py Wed Feb 01 23:12:20 2017 +0200 +++ b/Lib/test/test_gdb.py Thu Feb 02 14:09:46 2017 +0900 @@ -843,13 +843,13 @@ # Verify with "py-bt-full": gdb_output = self.get_stack_trace(cmd, breakpoint='time_gmtime', cmds_after_breakpoint=['py-bt-full'], ) - self.assertIn('#1 tp_name); + return NULL; + } + self = stack[0]; + if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), + (PyObject *)PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name, + self->ob_type->tp_name); + return NULL; + } + + // func must not leaked. So allocate on the stack. + result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self, + stack+1, nargs-1, kwnames); + result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); + return result; +} + static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) { Py_ssize_t argc; PyObject *self, *func, *result, **stack; diff -r aa7ac93d23b2 Objects/methodobject.c --- a/Objects/methodobject.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Objects/methodobject.c Thu Feb 02 14:09:46 2017 +0900 @@ -212,46 +212,38 @@ args, nargs, kwargs); result = _Py_CheckFunctionResult(func, result, NULL); return result; } PyObject * -_PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, - Py_ssize_t nargs, PyObject *kwnames) +_PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) { - PyCFunctionObject *func; - PyCFunction meth; - PyObject *self, *result; - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - int flags; + /* _PyMethodDef_RawFastCallKeywords() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!PyErr_Occurred()); - assert(func_obj != NULL); - assert(PyCFunction_Check(func_obj)); + assert(method != NULL); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - assert((nargs == 0 && nkwargs == 0) || args != NULL); /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - /* _PyCFunction_FastCallKeywords() must not be called with an exception - set, because it can clear it (directly or indirectly) and so the caller - loses its exception */ - assert(!PyErr_Occurred()); - - func = (PyCFunctionObject*)func_obj; - meth = PyCFunction_GET_FUNCTION(func); - self = PyCFunction_GET_SELF(func); - flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + PyCFunction meth = method->ml_meth; + int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + Py_ssize_t nkwargs = kwnames == NULL ? 0 : PyTuple_Size(kwnames); + PyObject *result; switch (flags) { case METH_NOARGS: if (nargs != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%zd given)", - func->m_ml->ml_name, nargs); + method->ml_name, nargs); return NULL; } if (nkwargs) { goto no_keyword_error; } @@ -260,13 +252,13 @@ break; case METH_O: if (nargs != 1) { PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", - func->m_ml->ml_name, nargs); + method->ml_name, nargs); return NULL; } if (nkwargs) { goto no_keyword_error; } @@ -323,22 +315,37 @@ PyErr_SetString(PyExc_SystemError, "Bad call flags in _PyCFunction_FastCallKeywords. " "METH_OLDARGS is no longer supported!"); return NULL; } - result = _Py_CheckFunctionResult(func_obj, result, NULL); return result; no_keyword_error: PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); + method->ml_name); return NULL; } +PyObject * +_PyCFunction_FastCallKeywords(PyObject *func, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *result; + + assert(func != NULL); + assert(PyCFunction_Check(func)); + + result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml, + PyCFunction_GET_SELF(func), + args, nargs, kwnames); + result = _Py_CheckFunctionResult(func, result, NULL); + return result; +} + /* Methods (the standard built-in methods, that is) */ static void meth_dealloc(PyCFunctionObject *m) { _PyObject_GC_UNTRACK(m); diff -r aa7ac93d23b2 Objects/object.c --- a/Objects/object.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Objects/object.c Thu Feb 02 14:09:46 2017 +0900 @@ -1057,14 +1057,14 @@ if (tp->tp_dict == NULL && PyType_Ready(tp) < 0) return 0; descr = _PyType_Lookup(tp, name); if (descr != NULL) { Py_INCREF(descr); - if (PyFunction_Check(descr)) { - /* A python method. */ + if (PyFunction_Check(descr) || + (Py_TYPE(descr) == &PyMethodDescr_Type)) { meth_found = 1; } else { f = descr->ob_type->tp_descr_get; if (f != NULL && PyDescr_IsData(descr)) { *method = f(descr, obj, (PyObject *)obj->ob_type); Py_DECREF(descr); diff -r aa7ac93d23b2 Python/ceval.c --- a/Python/ceval.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Python/ceval.c Thu Feb 02 14:09:46 2017 +0900 @@ -4840,12 +4840,17 @@ if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); stack = (*pp_stack) - nargs - nkwargs; C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } + else if (Py_TYPE(func) == &PyMethodDescr_Type) { + PyThreadState *tstate = PyThreadState_GET(); + stack = (*pp_stack) - nargs - nkwargs; + C_TRACE(x, _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames)); + } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* Optimize access to bound methods. Reuse the Python stack to pass 'self' as the first argument, replace 'func' with 'self'. It avoids the creation of a new temporary tuple for arguments (to replace func with self) when the method uses diff -r aa7ac93d23b2 Tools/gdb/libpython.py --- a/Tools/gdb/libpython.py Wed Feb 01 23:12:20 2017 +0200 +++ b/Tools/gdb/libpython.py Thu Feb 02 14:09:46 2017 +0900 @@ -1537,16 +1537,13 @@ caller = frame.name() if not caller: return False if caller in ('_PyCFunction_FastCallDict', '_PyCFunction_FastCallKeywords'): - if caller == '_PyCFunction_FastCallKeywords': - arg_name = 'func_obj' - else: - arg_name = 'func' + arg_name = 'func' # Within that frame: # "func" is the local containing the PyObject* of the # PyCFunctionObject instance # "f" is the same value, but cast to (PyCFunctionObject*) # "self" is the (PyObject*) of the 'self' try: