mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
bpo-36974: implement PEP 590 (GH-13185)
Co-authored-by: Jeroen Demeyer <J.Demeyer@UGent.be> Co-authored-by: Mark Shannon <mark@hotpy.org>
This commit is contained in:
parent
d30da5dd9a
commit
aacc77fbd7
22 changed files with 411 additions and 240 deletions
218
Objects/call.c
218
Objects/call.c
|
@ -5,6 +5,10 @@
|
|||
#include "frameobject.h"
|
||||
|
||||
|
||||
static PyObject *
|
||||
cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs);
|
||||
|
||||
|
||||
int
|
||||
_PyObject_HasFastCall(PyObject *callable)
|
||||
{
|
||||
|
@ -83,131 +87,132 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where)
|
|||
/* --- Core PyObject call functions ------------------------------- */
|
||||
|
||||
PyObject *
|
||||
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, Py_ssize_t nargs,
|
||||
PyObject *kwargs)
|
||||
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwargs)
|
||||
{
|
||||
/* _PyObject_FastCallDict() 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(callable != NULL);
|
||||
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
assert(nargs >= 0);
|
||||
assert(nargs == 0 || args != NULL);
|
||||
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||
|
||||
if (PyFunction_Check(callable)) {
|
||||
return _PyFunction_FastCallDict(callable, args, nargs, kwargs);
|
||||
vectorcallfunc func = _PyVectorcall_Function(callable);
|
||||
if (func == NULL) {
|
||||
/* Use tp_call instead */
|
||||
return _PyObject_MakeTpCall(callable, args, nargs, kwargs);
|
||||
}
|
||||
else if (PyCFunction_Check(callable)) {
|
||||
return _PyCFunction_FastCallDict(callable, args, nargs, kwargs);
|
||||
|
||||
PyObject *res;
|
||||
if (kwargs == NULL) {
|
||||
res = func(callable, args, nargsf, NULL);
|
||||
}
|
||||
else {
|
||||
PyObject *argstuple, *result;
|
||||
ternaryfunc call;
|
||||
|
||||
/* Slow-path: build a temporary tuple */
|
||||
call = callable->ob_type->tp_call;
|
||||
if (call == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
|
||||
callable->ob_type->tp_name);
|
||||
PyObject *kwnames;
|
||||
PyObject *const *newargs;
|
||||
if (_PyStack_UnpackDict(args, nargs, kwargs, &newargs, &kwnames) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
argstuple = _PyTuple_FromArray(args, nargs);
|
||||
if (argstuple == NULL) {
|
||||
return NULL;
|
||||
res = func(callable, newargs, nargs, kwnames);
|
||||
if (kwnames != NULL) {
|
||||
Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames) + nargs;
|
||||
for (i = 0; i < n; i++) {
|
||||
Py_DECREF(newargs[i]);
|
||||
}
|
||||
PyMem_Free((PyObject **)newargs);
|
||||
Py_DECREF(kwnames);
|
||||
}
|
||||
|
||||
if (Py_EnterRecursiveCall(" while calling a Python object")) {
|
||||
Py_DECREF(argstuple);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = (*call)(callable, argstuple, kwargs);
|
||||
|
||||
Py_LeaveRecursiveCall();
|
||||
Py_DECREF(argstuple);
|
||||
|
||||
result = _Py_CheckFunctionResult(callable, result, NULL);
|
||||
return result;
|
||||
}
|
||||
return _Py_CheckFunctionResult(callable, res, NULL);
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_PyObject_FastCallKeywords(PyObject *callable, PyObject *const *stack, Py_ssize_t nargs,
|
||||
PyObject *kwnames)
|
||||
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
|
||||
{
|
||||
/* _PyObject_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());
|
||||
/* Slow path: build a temporary tuple for positional arguments and a
|
||||
* temporary dictionary for keyword arguments (if any) */
|
||||
ternaryfunc call = Py_TYPE(callable)->tp_call;
|
||||
if (call == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
|
||||
Py_TYPE(callable)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(nargs >= 0);
|
||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||
|
||||
/* kwnames must only contains str strings, no subclass, and all keys must
|
||||
be unique: these checks are implemented in Python/ceval.c and
|
||||
_PyArg_ParseStackAndKeywords(). */
|
||||
|
||||
if (PyFunction_Check(callable)) {
|
||||
return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames);
|
||||
assert(nargs == 0 || args != NULL);
|
||||
assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords));
|
||||
PyObject *argstuple = _PyTuple_FromArray(args, nargs);
|
||||
if (argstuple == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (PyCFunction_Check(callable)) {
|
||||
return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames);
|
||||
|
||||
PyObject *kwdict;
|
||||
if (keywords == NULL || PyDict_Check(keywords)) {
|
||||
kwdict = keywords;
|
||||
}
|
||||
else {
|
||||
/* Slow-path: build a temporary tuple for positional arguments and a
|
||||
temporary dictionary for keyword arguments (if any) */
|
||||
|
||||
ternaryfunc call;
|
||||
PyObject *argstuple;
|
||||
PyObject *kwdict, *result;
|
||||
Py_ssize_t nkwargs;
|
||||
|
||||
nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||
|
||||
call = callable->ob_type->tp_call;
|
||||
if (call == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
|
||||
callable->ob_type->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
argstuple = _PyTuple_FromArray(stack, nargs);
|
||||
if (argstuple == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nkwargs > 0) {
|
||||
kwdict = _PyStack_AsDict(stack + nargs, kwnames);
|
||||
if (PyTuple_GET_SIZE(keywords)) {
|
||||
assert(args != NULL);
|
||||
kwdict = _PyStack_AsDict(args + nargs, keywords);
|
||||
if (kwdict == NULL) {
|
||||
Py_DECREF(argstuple);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
kwdict = NULL;
|
||||
keywords = kwdict = NULL;
|
||||
}
|
||||
|
||||
if (Py_EnterRecursiveCall(" while calling a Python object")) {
|
||||
Py_DECREF(argstuple);
|
||||
Py_XDECREF(kwdict);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = (*call)(callable, argstuple, kwdict);
|
||||
|
||||
Py_LeaveRecursiveCall();
|
||||
|
||||
Py_DECREF(argstuple);
|
||||
Py_XDECREF(kwdict);
|
||||
|
||||
result = _Py_CheckFunctionResult(callable, result, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
PyObject *result = NULL;
|
||||
if (Py_EnterRecursiveCall(" while calling a Python object") == 0)
|
||||
{
|
||||
result = call(callable, argstuple, kwdict);
|
||||
Py_LeaveRecursiveCall();
|
||||
}
|
||||
|
||||
Py_DECREF(argstuple);
|
||||
if (kwdict != keywords) {
|
||||
Py_DECREF(kwdict);
|
||||
}
|
||||
|
||||
result = _Py_CheckFunctionResult(callable, result, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
|
||||
{
|
||||
vectorcallfunc func = _PyVectorcall_Function(callable);
|
||||
if (func == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall",
|
||||
Py_TYPE(callable)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *const *args;
|
||||
Py_ssize_t nargs = PyTuple_GET_SIZE(tuple);
|
||||
PyObject *kwnames;
|
||||
if (_PyStack_UnpackDict(_PyTuple_ITEMS(tuple), nargs,
|
||||
kwargs, &args, &kwnames) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *result = func(callable, args, nargs, kwnames);
|
||||
if (kwnames != NULL) {
|
||||
Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames) + nargs;
|
||||
for (i = 0; i < n; i++) {
|
||||
Py_DECREF(args[i]);
|
||||
}
|
||||
PyMem_Free((PyObject **)args);
|
||||
Py_DECREF(kwnames);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,14 +229,13 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
|
|||
assert(PyTuple_Check(args));
|
||||
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||
|
||||
if (PyFunction_Check(callable)) {
|
||||
return _PyFunction_FastCallDict(callable,
|
||||
_PyTuple_ITEMS(args),
|
||||
PyTuple_GET_SIZE(args),
|
||||
kwargs);
|
||||
if (_PyVectorcall_Function(callable) != NULL) {
|
||||
return PyVectorcall_Call(callable, args, kwargs);
|
||||
}
|
||||
else if (PyCFunction_Check(callable)) {
|
||||
return PyCFunction_Call(callable, args, kwargs);
|
||||
/* This must be a METH_VARARGS function, otherwise we would be
|
||||
* in the previous case */
|
||||
return cfunction_call_varargs(callable, args, kwargs);
|
||||
}
|
||||
else {
|
||||
call = callable->ob_type->tp_call;
|
||||
|
@ -384,9 +388,10 @@ _PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack,
|
||||
Py_ssize_t nargs, PyObject *kwnames)
|
||||
_PyFunction_FastCallKeywords(PyObject *func, PyObject* const* stack,
|
||||
size_t nargsf, PyObject *kwnames)
|
||||
{
|
||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
||||
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
||||
|
@ -397,6 +402,7 @@ _PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack,
|
|||
Py_ssize_t nd;
|
||||
|
||||
assert(PyFunction_Check(func));
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
assert(nargs >= 0);
|
||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||
|
@ -725,13 +731,14 @@ exit:
|
|||
|
||||
PyObject *
|
||||
_PyCFunction_FastCallKeywords(PyObject *func,
|
||||
PyObject *const *args, Py_ssize_t nargs,
|
||||
PyObject *const *args, size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
PyObject *result;
|
||||
|
||||
assert(func != NULL);
|
||||
assert(PyCFunction_Check(func));
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
|
||||
result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml,
|
||||
PyCFunction_GET_SELF(func),
|
||||
|
@ -751,6 +758,7 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
|
|||
PyObject *self = PyCFunction_GET_SELF(func);
|
||||
PyObject *result;
|
||||
|
||||
assert(PyCFunction_GET_FLAGS(func) & METH_VARARGS);
|
||||
if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) {
|
||||
if (Py_EnterRecursiveCall(" while calling a Python object")) {
|
||||
return NULL;
|
||||
|
@ -783,18 +791,12 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
|
|||
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. */
|
||||
/* For METH_VARARGS, we cannot use vectorcall as the vectorcall pointer
|
||||
* is NULL. This is intentional, since vectorcall would be slower. */
|
||||
if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) {
|
||||
return cfunction_call_varargs(func, args, kwargs);
|
||||
}
|
||||
else {
|
||||
return _PyCFunction_FastCallDict(func,
|
||||
_PyTuple_ITEMS(args),
|
||||
PyTuple_GET_SIZE(args),
|
||||
kwargs);
|
||||
}
|
||||
return PyVectorcall_Call(func, args, kwargs);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue