diff options
Diffstat (limited to 'Objects/methodobject.c')
-rw-r--r-- | Objects/methodobject.c | 341 |
1 files changed, 222 insertions, 119 deletions
diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c2001f0169..d0fbefd550 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -77,163 +77,137 @@ PyCFunction_GetFlags(PyObject *op) return PyCFunction_GET_FLAGS(op); } -PyObject * -PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) +static PyObject * +cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) { - PyCFunctionObject* f = (PyCFunctionObject*)func; + assert(!PyErr_Occurred()); + PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); - PyObject *arg, *res; - Py_ssize_t size; - int flags; + PyObject *result; - /* PyCFunction_Call() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the - caller loses its exception */ - assert(!PyErr_Occurred()); + if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) { + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } - flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + result = (*(PyCFunctionWithKeywords)meth)(self, args, kwargs); - if (flags == (METH_VARARGS | METH_KEYWORDS)) { - res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds); - } - else if (flags == METH_FASTCALL) { - PyObject **stack = &PyTuple_GET_ITEM(args, 0); - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - res = _PyCFunction_FastCallDict(func, stack, nargs, kwds); + Py_LeaveRecursiveCall(); } else { - if (kwds != NULL && PyDict_Size(kwds) != 0) { + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - f->m_ml->ml_name); + ((PyCFunctionObject*)func)->m_ml->ml_name); return NULL; } - switch (flags) { - case METH_VARARGS: - res = (*meth)(self, args); - break; - - case METH_NOARGS: - size = PyTuple_GET_SIZE(args); - if (size != 0) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", - f->m_ml->ml_name, size); - return NULL; - } + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } - res = (*meth)(self, NULL); - break; + result = (*meth)(self, args); - case METH_O: - size = PyTuple_GET_SIZE(args); - if (size != 1) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%zd given)", - f->m_ml->ml_name, size); - return NULL; - } + Py_LeaveRecursiveCall(); + } - arg = PyTuple_GET_ITEM(args, 0); - res = (*meth)(self, arg); - break; + return _Py_CheckFunctionResult(func, result, NULL); +} - default: - PyErr_SetString(PyExc_SystemError, - "Bad call flags in PyCFunction_Call. " - "METH_OLDARGS is no longer supported!"); - return NULL; - } - } - return _Py_CheckFunctionResult(func, res, NULL); +PyObject * +PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs) +{ + /* first try METH_VARARGS to pass directly args tuple unchanged. + _PyMethodDef_RawFastCallDict() creates a new temporary tuple + for METH_VARARGS. */ + if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) { + return cfunction_call_varargs(func, args, kwargs); + } + else { + return _PyCFunction_FastCallDict(func, + &PyTuple_GET_ITEM(args, 0), + PyTuple_GET_SIZE(args), + kwargs); + } } PyObject * -_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, - PyObject *kwargs) +_PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args, + Py_ssize_t nargs, PyObject *kwargs) { - PyCFunctionObject *func = (PyCFunctionObject*)func_obj; - PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); - PyObject *result; - int flags; + /* _PyMethodDef_RawFastCallDict() 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(PyCFunction_Check(func)); - assert(func != NULL); + assert(method != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); - /* _PyCFunction_FastCallDict() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the - caller loses its exception */ - assert(!PyErr_Occurred()); + PyCFunction meth = method->ml_meth; + int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + PyObject *result = NULL; - flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } switch (flags) { case METH_NOARGS: - if (kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; - } - if (nargs != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%zd given)", - func->m_ml->ml_name, nargs); - return NULL; + method->ml_name, nargs); + goto exit; + } + + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + goto no_keyword_error; } result = (*meth) (self, NULL); break; case METH_O: - if (kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; - } - if (nargs != 1) { PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", - func->m_ml->ml_name, nargs); - return NULL; + method->ml_name, nargs); + goto exit; + } + + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + goto no_keyword_error; } result = (*meth) (self, args[0]); break; case METH_VARARGS: - case METH_VARARGS | METH_KEYWORDS: - { - /* Slow-path: create a temporary tuple */ - PyObject *tuple; - - if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; + if (!(flags & METH_KEYWORDS) + && kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + goto no_keyword_error; } + /* fall through next case */ - tuple = _PyStack_AsTuple(args, nargs); - if (tuple == NULL) { - return NULL; + case METH_VARARGS | METH_KEYWORDS: + { + /* Slow-path: create a temporary tuple for positional arguments */ + PyObject *argstuple = _PyStack_AsTuple(args, nargs); + if (argstuple == NULL) { + goto exit; } if (flags & METH_KEYWORDS) { - result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs); + result = (*(PyCFunctionWithKeywords)meth) (self, argstuple, kwargs); } else { - result = (*meth) (self, tuple); + result = (*meth) (self, argstuple); } - Py_DECREF(tuple); + Py_DECREF(argstuple); break; } @@ -243,9 +217,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *kwnames; _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; - stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj); - if (stack == NULL) { - return NULL; + if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) { + goto exit; } result = (*fastmeth) (self, stack, nargs, kwnames); @@ -258,42 +231,172 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, default: PyErr_SetString(PyExc_SystemError, - "Bad call flags in PyCFunction_Call. " + "Bad call flags in _PyMethodDef_RawFastCallDict. " "METH_OLDARGS is no longer supported!"); - return NULL; + goto exit; } - result = _Py_CheckFunctionResult(func_obj, result, NULL); + goto exit; +no_keyword_error: + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + method->ml_name, nargs); + +exit: + Py_LeaveRecursiveCall(); return result; } PyObject * -_PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, - Py_ssize_t nargs, PyObject *kwnames) +_PyCFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, + PyObject *kwargs) { - PyObject *kwdict, *result; - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + PyObject *result; + assert(func != NULL); assert(PyCFunction_Check(func)); + + result = _PyMethodDef_RawFastCallDict(((PyCFunctionObject*)func)->m_ml, + PyCFunction_GET_SELF(func), + args, nargs, kwargs); + result = _Py_CheckFunctionResult(func, result, NULL); + return result; +} + +PyObject * +_PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) +{ + /* _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(method != NULL); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - assert((nargs == 0 && nkwargs == 0) || stack != NULL); /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); - if (kwdict == NULL) { - return NULL; + 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 = NULL; + + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + + switch (flags) + { + case METH_NOARGS: + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + method->ml_name, nargs); + goto exit; + } + + if (nkwargs) { + goto no_keyword_error; + } + + result = (*meth) (self, NULL); + break; + + case METH_O: + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + method->ml_name, nargs); + goto exit; + } + + if (nkwargs) { + goto no_keyword_error; } + + result = (*meth) (self, args[0]); + break; + + case METH_FASTCALL: + /* Fast-path: avoid temporary dict to pass keyword arguments */ + result = ((_PyCFunctionFast)meth) (self, args, nargs, kwnames); + break; + + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + { + /* Slow-path: create a temporary tuple for positional arguments + and a temporary dict for keyword arguments */ + PyObject *argtuple; + + if (!(flags & METH_KEYWORDS) && nkwargs) { + goto no_keyword_error; + } + + argtuple = _PyStack_AsTuple(args, nargs); + if (argtuple == NULL) { + goto exit; + } + + if (flags & METH_KEYWORDS) { + PyObject *kwdict; + + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(args + nargs, kwnames); + if (kwdict == NULL) { + Py_DECREF(argtuple); + goto exit; + } + } + else { + kwdict = NULL; + } + + result = (*(PyCFunctionWithKeywords)meth) (self, argtuple, kwdict); + Py_XDECREF(kwdict); + } + else { + result = (*meth) (self, argtuple); + } + Py_DECREF(argtuple); + break; } - else { - kwdict = NULL; + + default: + PyErr_SetString(PyExc_SystemError, + "Bad call flags in _PyCFunction_FastCallKeywords. " + "METH_OLDARGS is no longer supported!"); + goto exit; } - result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict); - Py_XDECREF(kwdict); + goto exit; + +no_keyword_error: + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + method->ml_name); + +exit: + Py_LeaveRecursiveCall(); + return result; +} + +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; } |