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
|
@ -14,6 +14,7 @@ typedef struct {
|
||||||
PyObject *im_func; /* The callable object implementing the method */
|
PyObject *im_func; /* The callable object implementing the method */
|
||||||
PyObject *im_self; /* The instance it is bound to */
|
PyObject *im_self; /* The instance it is bound to */
|
||||||
PyObject *im_weakreflist; /* List of weak references */
|
PyObject *im_weakreflist; /* List of weak references */
|
||||||
|
vectorcallfunc vectorcall;
|
||||||
} PyMethodObject;
|
} PyMethodObject;
|
||||||
|
|
||||||
PyAPI_DATA(PyTypeObject) PyMethod_Type;
|
PyAPI_DATA(PyTypeObject) PyMethod_Type;
|
||||||
|
|
|
@ -47,7 +47,7 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
|
||||||
/* Suggested size (number of positional arguments) for arrays of PyObject*
|
/* Suggested size (number of positional arguments) for arrays of PyObject*
|
||||||
allocated on a C stack to avoid allocating memory on the heap memory. Such
|
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
|
array is used to pass positional arguments to call functions of the
|
||||||
_PyObject_FastCall() family.
|
_PyObject_Vectorcall() family.
|
||||||
|
|
||||||
The size is chosen to not abuse the C stack and so limit the risk of stack
|
The size is chosen to not abuse the C stack and so limit the risk of stack
|
||||||
overflow. The size is also chosen to allow using the small stack for most
|
overflow. The size is also chosen to allow using the small stack for most
|
||||||
|
@ -56,50 +56,103 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
|
||||||
#define _PY_FASTCALL_SMALL_STACK 5
|
#define _PY_FASTCALL_SMALL_STACK 5
|
||||||
|
|
||||||
/* Return 1 if callable supports FASTCALL calling convention for positional
|
/* Return 1 if callable supports FASTCALL calling convention for positional
|
||||||
arguments: see _PyObject_FastCallDict() and _PyObject_FastCallKeywords() */
|
arguments: see _PyObject_Vectorcall() and _PyObject_FastCallDict() */
|
||||||
PyAPI_FUNC(int) _PyObject_HasFastCall(PyObject *callable);
|
PyAPI_FUNC(int) _PyObject_HasFastCall(PyObject *callable);
|
||||||
|
|
||||||
/* Call the callable object 'callable' with the "fast call" calling convention:
|
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
|
||||||
args is a C array for positional arguments (nargs is the number of
|
PyObject *result,
|
||||||
positional arguments), kwargs is a dictionary for keyword arguments.
|
const char *where);
|
||||||
|
|
||||||
If nargs is equal to zero, args can be NULL. kwargs can be NULL.
|
/* === Vectorcall protocol (PEP 590) ============================= */
|
||||||
nargs must be greater or equal to zero.
|
|
||||||
|
/* Call callable using tp_call. Arguments are like _PyObject_Vectorcall()
|
||||||
|
or _PyObject_FastCallDict() (both forms are supported),
|
||||||
|
except that nargs is plainly the number of arguments without flags. */
|
||||||
|
PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
|
||||||
|
PyObject *callable,
|
||||||
|
PyObject *const *args, Py_ssize_t nargs,
|
||||||
|
PyObject *keywords);
|
||||||
|
|
||||||
|
#define PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1))
|
||||||
|
|
||||||
|
static inline Py_ssize_t
|
||||||
|
PyVectorcall_NARGS(size_t n)
|
||||||
|
{
|
||||||
|
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vectorcallfunc
|
||||||
|
_PyVectorcall_Function(PyObject *callable)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(callable);
|
||||||
|
if (!PyType_HasFeature(tp, _Py_TPFLAGS_HAVE_VECTORCALL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(PyCallable_Check(callable));
|
||||||
|
Py_ssize_t offset = tp->tp_vectorcall_offset;
|
||||||
|
assert(offset > 0);
|
||||||
|
vectorcallfunc *ptr = (vectorcallfunc *)(((char *)callable) + offset);
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call the callable object 'callable' with the "vectorcall" calling
|
||||||
|
convention.
|
||||||
|
|
||||||
|
args is a C array for positional arguments.
|
||||||
|
|
||||||
|
nargsf is the number of positional arguments plus optionally the flag
|
||||||
|
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
|
||||||
|
modify args[-1].
|
||||||
|
|
||||||
|
kwnames is a tuple of keyword names. The values of the keyword arguments
|
||||||
|
are stored in "args" after the positional arguments (note that the number
|
||||||
|
of keyword arguments does not change nargsf). kwnames can also be NULL if
|
||||||
|
there are no keyword arguments.
|
||||||
|
|
||||||
|
keywords must only contains str strings (no subclass), and all keys must
|
||||||
|
be unique.
|
||||||
|
|
||||||
Return the result on success. Raise an exception and return NULL on
|
Return the result on success. Raise an exception and return NULL on
|
||||||
error. */
|
error. */
|
||||||
|
static inline PyObject *
|
||||||
|
_PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
|
||||||
|
size_t nargsf, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
assert(kwnames == NULL || PyTuple_Check(kwnames));
|
||||||
|
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
|
||||||
|
vectorcallfunc func = _PyVectorcall_Function(callable);
|
||||||
|
if (func == NULL) {
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
|
return _PyObject_MakeTpCall(callable, args, nargs, kwnames);
|
||||||
|
}
|
||||||
|
PyObject *res = func(callable, args, nargsf, kwnames);
|
||||||
|
return _Py_CheckFunctionResult(callable, res, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same as _PyObject_Vectorcall except that keyword arguments are passed as
|
||||||
|
dict, which may be NULL if there are no keyword arguments. */
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
|
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
|
||||||
PyObject *callable,
|
PyObject *callable,
|
||||||
PyObject *const *args,
|
PyObject *const *args,
|
||||||
Py_ssize_t nargs,
|
size_t nargsf,
|
||||||
PyObject *kwargs);
|
PyObject *kwargs);
|
||||||
|
|
||||||
/* Call the callable object 'callable' with the "fast call" calling convention:
|
/* Call "callable" (which must support vectorcall) with positional arguments
|
||||||
args is a C array for positional arguments followed by values of
|
"tuple" and keyword arguments "dict". "dict" may also be NULL */
|
||||||
keyword arguments. Keys of keyword arguments are stored as a tuple
|
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
|
||||||
of strings in kwnames. nargs is the number of positional parameters at
|
|
||||||
the beginning of stack. The size of kwnames gives the number of keyword
|
|
||||||
values in the stack after positional arguments.
|
|
||||||
|
|
||||||
kwnames must only contains str strings, no subclass, and all keys must
|
/* Same as _PyObject_Vectorcall except without keyword arguments */
|
||||||
be unique.
|
static inline PyObject *
|
||||||
|
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
|
||||||
|
{
|
||||||
|
return _PyObject_Vectorcall(func, args, (size_t)nargs, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
If nargs is equal to zero and there is no keyword argument (kwnames is
|
/* Call a callable without any arguments */
|
||||||
NULL or its size is zero), args can be NULL.
|
static inline PyObject *
|
||||||
|
_PyObject_CallNoArg(PyObject *func) {
|
||||||
Return the result on success. Raise an exception and return NULL on
|
return _PyObject_Vectorcall(func, NULL, 0, NULL);
|
||||||
error. */
|
}
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_FastCallKeywords(
|
|
||||||
PyObject *callable,
|
|
||||||
PyObject *const *args,
|
|
||||||
Py_ssize_t nargs,
|
|
||||||
PyObject *kwnames);
|
|
||||||
|
|
||||||
#define _PyObject_FastCall(func, args, nargs) \
|
|
||||||
_PyObject_FastCallDict((func), (args), (nargs), NULL)
|
|
||||||
|
|
||||||
#define _PyObject_CallNoArg(func) \
|
|
||||||
_PyObject_FastCallDict((func), NULL, 0, NULL)
|
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
|
PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
|
||||||
PyObject *callable,
|
PyObject *callable,
|
||||||
|
@ -113,10 +166,6 @@ PyAPI_FUNC(PyObject *) _PyObject_FastCall_Prepend(
|
||||||
PyObject *const *args,
|
PyObject *const *args,
|
||||||
Py_ssize_t nargs);
|
Py_ssize_t nargs);
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
|
|
||||||
PyObject *result,
|
|
||||||
const char *where);
|
|
||||||
|
|
||||||
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
|
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
|
||||||
as the method name. */
|
as the method name. */
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
|
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
|
||||||
|
|
|
@ -55,6 +55,9 @@ typedef struct bufferinfo {
|
||||||
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
|
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
|
||||||
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
|
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
|
||||||
|
|
||||||
|
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
|
||||||
|
size_t nargsf, PyObject *kwnames);
|
||||||
|
|
||||||
/* Maximum number of dimensions */
|
/* Maximum number of dimensions */
|
||||||
#define PyBUF_MAX_NDIM 64
|
#define PyBUF_MAX_NDIM 64
|
||||||
|
|
||||||
|
@ -167,12 +170,9 @@ typedef struct {
|
||||||
releasebufferproc bf_releasebuffer;
|
releasebufferproc bf_releasebuffer;
|
||||||
} PyBufferProcs;
|
} PyBufferProcs;
|
||||||
|
|
||||||
/* We can't provide a full compile-time check that limited-API
|
/* Allow printfunc in the tp_vectorcall_offset slot for
|
||||||
users won't implement tp_print. However, not defining printfunc
|
* backwards-compatibility */
|
||||||
and making tp_print of a different function pointer type
|
typedef Py_ssize_t printfunc;
|
||||||
if Py_LIMITED_API is set should at least cause a warning
|
|
||||||
in most cases. */
|
|
||||||
typedef int (*printfunc)(PyObject *, FILE *, int);
|
|
||||||
|
|
||||||
typedef struct _typeobject {
|
typedef struct _typeobject {
|
||||||
PyObject_VAR_HEAD
|
PyObject_VAR_HEAD
|
||||||
|
@ -182,7 +182,7 @@ typedef struct _typeobject {
|
||||||
/* Methods to implement standard operations */
|
/* Methods to implement standard operations */
|
||||||
|
|
||||||
destructor tp_dealloc;
|
destructor tp_dealloc;
|
||||||
printfunc tp_print;
|
Py_ssize_t tp_vectorcall_offset;
|
||||||
getattrfunc tp_getattr;
|
getattrfunc tp_getattr;
|
||||||
setattrfunc tp_setattr;
|
setattrfunc tp_setattr;
|
||||||
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
|
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
|
||||||
|
@ -254,6 +254,7 @@ typedef struct _typeobject {
|
||||||
unsigned int tp_version_tag;
|
unsigned int tp_version_tag;
|
||||||
|
|
||||||
destructor tp_finalize;
|
destructor tp_finalize;
|
||||||
|
vectorcallfunc tp_vectorcall;
|
||||||
|
|
||||||
#ifdef COUNT_ALLOCS
|
#ifdef COUNT_ALLOCS
|
||||||
/* these must be last and never explicitly initialized */
|
/* these must be last and never explicitly initialized */
|
||||||
|
|
|
@ -53,6 +53,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyDescr_COMMON;
|
PyDescr_COMMON;
|
||||||
PyMethodDef *d_method;
|
PyMethodDef *d_method;
|
||||||
|
vectorcallfunc vectorcall;
|
||||||
} PyMethodDescrObject;
|
} PyMethodDescrObject;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -92,7 +93,7 @@ PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *,
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyMethodDescr_FastCallKeywords(
|
PyAPI_FUNC(PyObject *) _PyMethodDescr_FastCallKeywords(
|
||||||
PyObject *descrobj, PyObject *const *stack, Py_ssize_t nargs, PyObject *kwnames);
|
PyObject *descrobj, PyObject *const *args, size_t nargsf, PyObject *kwnames);
|
||||||
PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *,
|
PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *,
|
||||||
struct wrapperbase *, void *);
|
struct wrapperbase *, void *);
|
||||||
#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL)
|
#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL)
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef struct {
|
||||||
PyObject *func_module; /* The __module__ attribute, can be anything */
|
PyObject *func_module; /* The __module__ attribute, can be anything */
|
||||||
PyObject *func_annotations; /* Annotations, a dict or NULL */
|
PyObject *func_annotations; /* Annotations, a dict or NULL */
|
||||||
PyObject *func_qualname; /* The qualified name */
|
PyObject *func_qualname; /* The qualified name */
|
||||||
|
vectorcallfunc vectorcall;
|
||||||
|
|
||||||
/* Invariant:
|
/* Invariant:
|
||||||
* func_closure contains the bindings for func_code->co_freevars, so
|
* func_closure contains the bindings for func_code->co_freevars, so
|
||||||
|
@ -68,7 +69,7 @@ PyAPI_FUNC(PyObject *) _PyFunction_FastCallDict(
|
||||||
PyAPI_FUNC(PyObject *) _PyFunction_FastCallKeywords(
|
PyAPI_FUNC(PyObject *) _PyFunction_FastCallKeywords(
|
||||||
PyObject *func,
|
PyObject *func,
|
||||||
PyObject *const *stack,
|
PyObject *const *stack,
|
||||||
Py_ssize_t nargs,
|
size_t nargsf,
|
||||||
PyObject *kwnames);
|
PyObject *kwnames);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func,
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyCFunction_FastCallKeywords(PyObject *func,
|
PyAPI_FUNC(PyObject *) _PyCFunction_FastCallKeywords(PyObject *func,
|
||||||
PyObject *const *stack,
|
PyObject *const *stack,
|
||||||
Py_ssize_t nargs,
|
size_t nargsf,
|
||||||
PyObject *kwnames);
|
PyObject *kwnames);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -105,6 +105,7 @@ typedef struct {
|
||||||
PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
|
PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */
|
||||||
PyObject *m_module; /* The __module__ attribute, can be anything */
|
PyObject *m_module; /* The __module__ attribute, can be anything */
|
||||||
PyObject *m_weakreflist; /* List of weak references */
|
PyObject *m_weakreflist; /* List of weak references */
|
||||||
|
vectorcallfunc vectorcall;
|
||||||
} PyCFunctionObject;
|
} PyCFunctionObject;
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict(
|
PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict(
|
||||||
|
|
|
@ -291,6 +291,11 @@ given type object has a specified feature.
|
||||||
/* Set if the type allows subclassing */
|
/* Set if the type allows subclassing */
|
||||||
#define Py_TPFLAGS_BASETYPE (1UL << 10)
|
#define Py_TPFLAGS_BASETYPE (1UL << 10)
|
||||||
|
|
||||||
|
/* Set if the type implements the vectorcall protocol (PEP 590) */
|
||||||
|
#ifndef Py_LIMITED_API
|
||||||
|
#define _Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Set if the type is 'ready' -- fully initialized */
|
/* Set if the type is 'ready' -- fully initialized */
|
||||||
#define Py_TPFLAGS_READY (1UL << 12)
|
#define Py_TPFLAGS_READY (1UL << 12)
|
||||||
|
|
||||||
|
|
|
@ -402,7 +402,7 @@ class FastCallTests(unittest.TestCase):
|
||||||
result = _testcapi.pyobject_fastcall(func, None)
|
result = _testcapi.pyobject_fastcall(func, None)
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
def test_fastcall_dict(self):
|
def test_vectorcall_dict(self):
|
||||||
# Test _PyObject_FastCallDict()
|
# Test _PyObject_FastCallDict()
|
||||||
|
|
||||||
for func, args, expected in self.CALLS_POSARGS:
|
for func, args, expected in self.CALLS_POSARGS:
|
||||||
|
@ -429,33 +429,33 @@ class FastCallTests(unittest.TestCase):
|
||||||
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
|
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
def test_fastcall_keywords(self):
|
def test_vectorcall(self):
|
||||||
# Test _PyObject_FastCallKeywords()
|
# Test _PyObject_Vectorcall()
|
||||||
|
|
||||||
for func, args, expected in self.CALLS_POSARGS:
|
for func, args, expected in self.CALLS_POSARGS:
|
||||||
with self.subTest(func=func, args=args):
|
with self.subTest(func=func, args=args):
|
||||||
# kwnames=NULL
|
# kwnames=NULL
|
||||||
result = _testcapi.pyobject_fastcallkeywords(func, args, None)
|
result = _testcapi.pyobject_vectorcall(func, args, None)
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
# kwnames=()
|
# kwnames=()
|
||||||
result = _testcapi.pyobject_fastcallkeywords(func, args, ())
|
result = _testcapi.pyobject_vectorcall(func, args, ())
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
# kwnames=NULL
|
# kwnames=NULL
|
||||||
result = _testcapi.pyobject_fastcallkeywords(func, None, None)
|
result = _testcapi.pyobject_vectorcall(func, None, None)
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
# kwnames=()
|
# kwnames=()
|
||||||
result = _testcapi.pyobject_fastcallkeywords(func, None, ())
|
result = _testcapi.pyobject_vectorcall(func, None, ())
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
for func, args, kwargs, expected in self.CALLS_KWARGS:
|
for func, args, kwargs, expected in self.CALLS_KWARGS:
|
||||||
with self.subTest(func=func, args=args, kwargs=kwargs):
|
with self.subTest(func=func, args=args, kwargs=kwargs):
|
||||||
kwnames = tuple(kwargs.keys())
|
kwnames = tuple(kwargs.keys())
|
||||||
args = args + tuple(kwargs.values())
|
args = args + tuple(kwargs.values())
|
||||||
result = _testcapi.pyobject_fastcallkeywords(func, args, kwnames)
|
result = _testcapi.pyobject_vectorcall(func, args, kwnames)
|
||||||
self.check_result(result, expected)
|
self.check_result(result, expected)
|
||||||
|
|
||||||
def test_fastcall_clearing_dict(self):
|
def test_fastcall_clearing_dict(self):
|
||||||
|
|
|
@ -34,6 +34,11 @@ def testfunction(self):
|
||||||
"""some doc"""
|
"""some doc"""
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def testfunction_kw(self, *, kw):
|
||||||
|
"""some doc"""
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class InstanceMethod:
|
class InstanceMethod:
|
||||||
id = _testcapi.instancemethod(id)
|
id = _testcapi.instancemethod(id)
|
||||||
testfunction = _testcapi.instancemethod(testfunction)
|
testfunction = _testcapi.instancemethod(testfunction)
|
||||||
|
@ -479,6 +484,48 @@ class TestPEP590(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
|
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
|
||||||
|
|
||||||
|
def test_vectorcall(self):
|
||||||
|
# Test a bunch of different ways to call objects:
|
||||||
|
# 1. normal call
|
||||||
|
# 2. vectorcall using _PyObject_Vectorcall()
|
||||||
|
# 3. vectorcall using PyVectorcall_Call()
|
||||||
|
# 4. call as bound method
|
||||||
|
# 5. call using functools.partial
|
||||||
|
|
||||||
|
# A list of (function, args, kwargs, result) calls to test
|
||||||
|
calls = [(len, (range(42),), {}, 42),
|
||||||
|
(list.append, ([], 0), {}, None),
|
||||||
|
([].append, (0,), {}, None),
|
||||||
|
(sum, ([36],), {"start":6}, 42),
|
||||||
|
(testfunction, (42,), {}, 42),
|
||||||
|
(testfunction_kw, (42,), {"kw":None}, 42)]
|
||||||
|
|
||||||
|
from _testcapi import pyobject_vectorcall, pyvectorcall_call
|
||||||
|
from types import MethodType
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
def vectorcall(func, args, kwargs):
|
||||||
|
args = *args, *kwargs.values()
|
||||||
|
kwnames = tuple(kwargs)
|
||||||
|
return pyobject_vectorcall(func, args, kwnames)
|
||||||
|
|
||||||
|
for (func, args, kwargs, expected) in calls:
|
||||||
|
with self.subTest(str(func)):
|
||||||
|
args1 = args[1:]
|
||||||
|
meth = MethodType(func, args[0])
|
||||||
|
wrapped = partial(func)
|
||||||
|
if not kwargs:
|
||||||
|
self.assertEqual(expected, func(*args))
|
||||||
|
self.assertEqual(expected, pyobject_vectorcall(func, args, None))
|
||||||
|
self.assertEqual(expected, pyvectorcall_call(func, args))
|
||||||
|
self.assertEqual(expected, meth(*args1))
|
||||||
|
self.assertEqual(expected, wrapped(*args))
|
||||||
|
self.assertEqual(expected, func(*args, **kwargs))
|
||||||
|
self.assertEqual(expected, vectorcall(func, args, kwargs))
|
||||||
|
self.assertEqual(expected, pyvectorcall_call(func, args, kwargs))
|
||||||
|
self.assertEqual(expected, meth(*args1, **kwargs))
|
||||||
|
self.assertEqual(expected, wrapped(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
class SubinterpreterTest(unittest.TestCase):
|
class SubinterpreterTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -1064,7 +1064,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
# buffer
|
# buffer
|
||||||
# XXX
|
# XXX
|
||||||
# builtin_function_or_method
|
# builtin_function_or_method
|
||||||
check(len, size('4P')) # XXX check layout
|
check(len, size('5P'))
|
||||||
# bytearray
|
# bytearray
|
||||||
samples = [b'', b'u'*100000]
|
samples = [b'', b'u'*100000]
|
||||||
for sample in samples:
|
for sample in samples:
|
||||||
|
@ -1095,7 +1095,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
# complex
|
# complex
|
||||||
check(complex(0,1), size('2d'))
|
check(complex(0,1), size('2d'))
|
||||||
# method_descriptor (descriptor object)
|
# method_descriptor (descriptor object)
|
||||||
check(str.lower, size('3PP'))
|
check(str.lower, size('3PPP'))
|
||||||
# classmethod_descriptor (descriptor object)
|
# classmethod_descriptor (descriptor object)
|
||||||
# XXX
|
# XXX
|
||||||
# member_descriptor (descriptor object)
|
# member_descriptor (descriptor object)
|
||||||
|
@ -1164,7 +1164,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
||||||
# function
|
# function
|
||||||
def func(): pass
|
def func(): pass
|
||||||
check(func, size('12P'))
|
check(func, size('13P'))
|
||||||
class c():
|
class c():
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def foo():
|
def foo():
|
||||||
|
@ -1259,7 +1259,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check((1,2,3), vsize('') + 3*self.P)
|
check((1,2,3), vsize('') + 3*self.P)
|
||||||
# type
|
# type
|
||||||
# static type: PyTypeObject
|
# static type: PyTypeObject
|
||||||
fmt = 'P2n15Pl4Pn9Pn11PIP'
|
fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
|
||||||
if hasattr(sys, 'getcounts'):
|
if hasattr(sys, 'getcounts'):
|
||||||
fmt += '3n2P'
|
fmt += '3n2P'
|
||||||
s = vsize(fmt)
|
s = vsize(fmt)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Implement :pep:`590`: Vectorcall: a fast calling protocol for CPython.
|
||||||
|
This is a new protocol to optimize calls of custom callable objects.
|
|
@ -367,8 +367,7 @@ call_soon(PyObject *loop, PyObject *func, PyObject *arg, PyObject *ctx)
|
||||||
}
|
}
|
||||||
stack[nargs] = (PyObject *)ctx;
|
stack[nargs] = (PyObject *)ctx;
|
||||||
|
|
||||||
handle = _PyObject_FastCallKeywords(
|
handle = _PyObject_Vectorcall(callable, stack, nargs, context_kwname);
|
||||||
callable, stack, nargs, context_kwname);
|
|
||||||
Py_DECREF(callable);
|
Py_DECREF(callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2805,8 +2804,7 @@ set_exception:
|
||||||
PyObject *stack[2];
|
PyObject *stack[2];
|
||||||
stack[0] = wrapper;
|
stack[0] = wrapper;
|
||||||
stack[1] = (PyObject *)task->task_context;
|
stack[1] = (PyObject *)task->task_context;
|
||||||
res = _PyObject_FastCallKeywords(
|
res = _PyObject_Vectorcall(add_cb, stack, 1, context_kwname);
|
||||||
add_cb, stack, 1, context_kwname);
|
|
||||||
Py_DECREF(add_cb);
|
Py_DECREF(add_cb);
|
||||||
Py_DECREF(wrapper);
|
Py_DECREF(wrapper);
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
|
|
|
@ -4713,7 +4713,7 @@ test_pyobject_fastcalldict(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
test_pyobject_fastcallkeywords(PyObject *self, PyObject *args)
|
test_pyobject_vectorcall(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *func, *func_args, *kwnames = NULL;
|
PyObject *func, *func_args, *kwnames = NULL;
|
||||||
PyObject **stack;
|
PyObject **stack;
|
||||||
|
@ -4742,7 +4742,31 @@ test_pyobject_fastcallkeywords(PyObject *self, PyObject *args)
|
||||||
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
|
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
|
return _PyObject_Vectorcall(func, stack, nargs, kwnames);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_pyvectorcall_call(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *func;
|
||||||
|
PyObject *argstuple;
|
||||||
|
PyObject *kwargs = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyTuple_Check(argstuple)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "args must be a tuple");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (kwargs != NULL && !PyDict_Check(kwargs)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyVectorcall_Call(func, argstuple, kwargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5230,7 +5254,8 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
|
{"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
|
||||||
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
|
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
|
||||||
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
|
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
|
||||||
{"pyobject_fastcallkeywords", test_pyobject_fastcallkeywords, METH_VARARGS},
|
{"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
|
||||||
|
{"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
|
||||||
{"stack_pointer", stack_pointer, METH_NOARGS},
|
{"stack_pointer", stack_pointer, METH_NOARGS},
|
||||||
#ifdef W_STOPCODE
|
#ifdef W_STOPCODE
|
||||||
{"W_STOPCODE", py_w_stopcode, METH_VARARGS},
|
{"W_STOPCODE", py_w_stopcode, METH_VARARGS},
|
||||||
|
|
218
Objects/call.c
218
Objects/call.c
|
@ -5,6 +5,10 @@
|
||||||
#include "frameobject.h"
|
#include "frameobject.h"
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs);
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyObject_HasFastCall(PyObject *callable)
|
_PyObject_HasFastCall(PyObject *callable)
|
||||||
{
|
{
|
||||||
|
@ -83,131 +87,132 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where)
|
||||||
/* --- Core PyObject call functions ------------------------------- */
|
/* --- Core PyObject call functions ------------------------------- */
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, Py_ssize_t nargs,
|
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
|
||||||
PyObject *kwargs)
|
size_t nargsf, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
/* _PyObject_FastCallDict() must not be called with an exception set,
|
/* _PyObject_FastCallDict() must not be called with an exception set,
|
||||||
because it can clear it (directly or indirectly) and so the
|
because it can clear it (directly or indirectly) and so the
|
||||||
caller loses its exception */
|
caller loses its exception */
|
||||||
assert(!PyErr_Occurred());
|
assert(!PyErr_Occurred());
|
||||||
|
|
||||||
assert(callable != NULL);
|
assert(callable != NULL);
|
||||||
|
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
assert(nargs >= 0);
|
assert(nargs >= 0);
|
||||||
assert(nargs == 0 || args != NULL);
|
assert(nargs == 0 || args != NULL);
|
||||||
assert(kwargs == NULL || PyDict_Check(kwargs));
|
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||||
|
|
||||||
if (PyFunction_Check(callable)) {
|
vectorcallfunc func = _PyVectorcall_Function(callable);
|
||||||
return _PyFunction_FastCallDict(callable, args, nargs, kwargs);
|
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 {
|
else {
|
||||||
PyObject *argstuple, *result;
|
PyObject *kwnames;
|
||||||
ternaryfunc call;
|
PyObject *const *newargs;
|
||||||
|
if (_PyStack_UnpackDict(args, nargs, kwargs, &newargs, &kwnames) < 0) {
|
||||||
/* 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);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
res = func(callable, newargs, nargs, kwnames);
|
||||||
argstuple = _PyTuple_FromArray(args, nargs);
|
if (kwnames != NULL) {
|
||||||
if (argstuple == NULL) {
|
Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames) + nargs;
|
||||||
return NULL;
|
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 *
|
||||||
_PyObject_FastCallKeywords(PyObject *callable, PyObject *const *stack, Py_ssize_t nargs,
|
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
|
||||||
PyObject *kwnames)
|
|
||||||
{
|
{
|
||||||
/* _PyObject_FastCallKeywords() must not be called with an exception set,
|
/* Slow path: build a temporary tuple for positional arguments and a
|
||||||
because it can clear it (directly or indirectly) and so the
|
* temporary dictionary for keyword arguments (if any) */
|
||||||
caller loses its exception */
|
ternaryfunc call = Py_TYPE(callable)->tp_call;
|
||||||
assert(!PyErr_Occurred());
|
if (call == NULL) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
|
||||||
|
Py_TYPE(callable)->tp_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
assert(nargs >= 0);
|
assert(nargs >= 0);
|
||||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
assert(nargs == 0 || args != NULL);
|
||||||
|
assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords));
|
||||||
/* kwnames must only contains str strings, no subclass, and all keys must
|
PyObject *argstuple = _PyTuple_FromArray(args, nargs);
|
||||||
be unique: these checks are implemented in Python/ceval.c and
|
if (argstuple == NULL) {
|
||||||
_PyArg_ParseStackAndKeywords(). */
|
return NULL;
|
||||||
|
|
||||||
if (PyFunction_Check(callable)) {
|
|
||||||
return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames);
|
|
||||||
}
|
}
|
||||||
if (PyCFunction_Check(callable)) {
|
|
||||||
return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames);
|
PyObject *kwdict;
|
||||||
|
if (keywords == NULL || PyDict_Check(keywords)) {
|
||||||
|
kwdict = keywords;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Slow-path: build a temporary tuple for positional arguments and a
|
if (PyTuple_GET_SIZE(keywords)) {
|
||||||
temporary dictionary for keyword arguments (if any) */
|
assert(args != NULL);
|
||||||
|
kwdict = _PyStack_AsDict(args + nargs, keywords);
|
||||||
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 (kwdict == NULL) {
|
if (kwdict == NULL) {
|
||||||
Py_DECREF(argstuple);
|
Py_DECREF(argstuple);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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(PyTuple_Check(args));
|
||||||
assert(kwargs == NULL || PyDict_Check(kwargs));
|
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||||
|
|
||||||
if (PyFunction_Check(callable)) {
|
if (_PyVectorcall_Function(callable) != NULL) {
|
||||||
return _PyFunction_FastCallDict(callable,
|
return PyVectorcall_Call(callable, args, kwargs);
|
||||||
_PyTuple_ITEMS(args),
|
|
||||||
PyTuple_GET_SIZE(args),
|
|
||||||
kwargs);
|
|
||||||
}
|
}
|
||||||
else if (PyCFunction_Check(callable)) {
|
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 {
|
else {
|
||||||
call = callable->ob_type->tp_call;
|
call = callable->ob_type->tp_call;
|
||||||
|
@ -384,9 +388,10 @@ _PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack,
|
_PyFunction_FastCallKeywords(PyObject *func, PyObject* const* stack,
|
||||||
Py_ssize_t nargs, PyObject *kwnames)
|
size_t nargsf, PyObject *kwnames)
|
||||||
{
|
{
|
||||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
||||||
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
||||||
|
@ -397,6 +402,7 @@ _PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack,
|
||||||
Py_ssize_t nd;
|
Py_ssize_t nd;
|
||||||
|
|
||||||
assert(PyFunction_Check(func));
|
assert(PyFunction_Check(func));
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
assert(nargs >= 0);
|
assert(nargs >= 0);
|
||||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||||
|
@ -725,13 +731,14 @@ exit:
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyCFunction_FastCallKeywords(PyObject *func,
|
_PyCFunction_FastCallKeywords(PyObject *func,
|
||||||
PyObject *const *args, Py_ssize_t nargs,
|
PyObject *const *args, size_t nargsf,
|
||||||
PyObject *kwnames)
|
PyObject *kwnames)
|
||||||
{
|
{
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
assert(func != NULL);
|
assert(func != NULL);
|
||||||
assert(PyCFunction_Check(func));
|
assert(PyCFunction_Check(func));
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
|
|
||||||
result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml,
|
result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml,
|
||||||
PyCFunction_GET_SELF(func),
|
PyCFunction_GET_SELF(func),
|
||||||
|
@ -751,6 +758,7 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
|
||||||
PyObject *self = PyCFunction_GET_SELF(func);
|
PyObject *self = PyCFunction_GET_SELF(func);
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
|
assert(PyCFunction_GET_FLAGS(func) & METH_VARARGS);
|
||||||
if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) {
|
if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) {
|
||||||
if (Py_EnterRecursiveCall(" while calling a Python object")) {
|
if (Py_EnterRecursiveCall(" while calling a Python object")) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -783,18 +791,12 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs)
|
PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
/* first try METH_VARARGS to pass directly args tuple unchanged.
|
/* For METH_VARARGS, we cannot use vectorcall as the vectorcall pointer
|
||||||
_PyMethodDef_RawFastCallDict() creates a new temporary tuple
|
* is NULL. This is intentional, since vectorcall would be slower. */
|
||||||
for METH_VARARGS. */
|
|
||||||
if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) {
|
if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) {
|
||||||
return cfunction_call_varargs(func, args, kwargs);
|
return cfunction_call_varargs(func, args, kwargs);
|
||||||
}
|
}
|
||||||
else {
|
return PyVectorcall_Call(func, args, kwargs);
|
||||||
return _PyCFunction_FastCallDict(func,
|
|
||||||
_PyTuple_ITEMS(args),
|
|
||||||
PyTuple_GET_SIZE(args),
|
|
||||||
kwargs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,45 @@ PyMethod_Self(PyObject *im)
|
||||||
return ((PyMethodObject *)im)->im_self;
|
return ((PyMethodObject *)im)->im_self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
method_vectorcall(PyObject *method, PyObject *const *args,
|
||||||
|
size_t nargsf, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
assert(Py_TYPE(method) == &PyMethod_Type);
|
||||||
|
PyObject *self, *func, *result;
|
||||||
|
self = PyMethod_GET_SELF(method);
|
||||||
|
func = PyMethod_GET_FUNCTION(method);
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
|
|
||||||
|
if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) {
|
||||||
|
/* PY_VECTORCALL_ARGUMENTS_OFFSET is set, so we are allowed to mutate the vector */
|
||||||
|
PyObject **newargs = (PyObject**)args - 1;
|
||||||
|
nargs += 1;
|
||||||
|
PyObject *tmp = newargs[0];
|
||||||
|
newargs[0] = self;
|
||||||
|
result = _PyObject_Vectorcall(func, newargs, nargs, kwnames);
|
||||||
|
newargs[0] = tmp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||||
|
PyObject **newargs;
|
||||||
|
Py_ssize_t totalargs = nargs + nkwargs;
|
||||||
|
newargs = PyMem_Malloc((totalargs+1) * sizeof(PyObject *));
|
||||||
|
if (newargs == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* use borrowed references */
|
||||||
|
newargs[0] = self;
|
||||||
|
memcpy(newargs + 1, args, totalargs * sizeof(PyObject *));
|
||||||
|
result = _PyObject_Vectorcall(func, newargs, nargs+1, kwnames);
|
||||||
|
PyMem_Free(newargs);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Method objects are used for bound instance methods returned by
|
/* Method objects are used for bound instance methods returned by
|
||||||
instancename.methodname. ClassName.methodname returns an ordinary
|
instancename.methodname. ClassName.methodname returns an ordinary
|
||||||
function.
|
function.
|
||||||
|
@ -69,6 +108,7 @@ PyMethod_New(PyObject *func, PyObject *self)
|
||||||
im->im_func = func;
|
im->im_func = func;
|
||||||
Py_XINCREF(self);
|
Py_XINCREF(self);
|
||||||
im->im_self = self;
|
im->im_self = self;
|
||||||
|
im->vectorcall = method_vectorcall;
|
||||||
_PyObject_GC_TRACK(im);
|
_PyObject_GC_TRACK(im);
|
||||||
return (PyObject *)im;
|
return (PyObject *)im;
|
||||||
}
|
}
|
||||||
|
@ -309,7 +349,7 @@ PyTypeObject PyMethod_Type = {
|
||||||
sizeof(PyMethodObject),
|
sizeof(PyMethodObject),
|
||||||
0,
|
0,
|
||||||
(destructor)method_dealloc, /* tp_dealloc */
|
(destructor)method_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
offsetof(PyMethodObject, vectorcall), /* tp_vectorcall_offset */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
0, /* tp_setattr */
|
0, /* tp_setattr */
|
||||||
0, /* tp_reserved */
|
0, /* tp_reserved */
|
||||||
|
@ -323,7 +363,8 @@ PyTypeObject PyMethod_Type = {
|
||||||
method_getattro, /* tp_getattro */
|
method_getattro, /* tp_getattro */
|
||||||
PyObject_GenericSetAttr, /* tp_setattro */
|
PyObject_GenericSetAttr, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||||
|
_Py_TPFLAGS_HAVE_VECTORCALL, /* tp_flags */
|
||||||
method_doc, /* tp_doc */
|
method_doc, /* tp_doc */
|
||||||
(traverseproc)method_traverse, /* tp_traverse */
|
(traverseproc)method_traverse, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
|
|
|
@ -265,13 +265,14 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
|
||||||
// same to methoddescr_call(), but use FASTCALL convention.
|
// same to methoddescr_call(), but use FASTCALL convention.
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyMethodDescr_FastCallKeywords(PyObject *descrobj,
|
_PyMethodDescr_FastCallKeywords(PyObject *descrobj,
|
||||||
PyObject *const *args, Py_ssize_t nargs,
|
PyObject *const *args, size_t nargsf,
|
||||||
PyObject *kwnames)
|
PyObject *kwnames)
|
||||||
{
|
{
|
||||||
assert(Py_TYPE(descrobj) == &PyMethodDescr_Type);
|
assert(Py_TYPE(descrobj) == &PyMethodDescr_Type);
|
||||||
PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj;
|
PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj;
|
||||||
PyObject *self, *result;
|
PyObject *self, *result;
|
||||||
|
|
||||||
|
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||||
/* Make sure that the first argument is acceptable as 'self' */
|
/* Make sure that the first argument is acceptable as 'self' */
|
||||||
if (nargs < 1) {
|
if (nargs < 1) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
@ -542,7 +543,7 @@ PyTypeObject PyMethodDescr_Type = {
|
||||||
sizeof(PyMethodDescrObject),
|
sizeof(PyMethodDescrObject),
|
||||||
0,
|
0,
|
||||||
(destructor)descr_dealloc, /* tp_dealloc */
|
(destructor)descr_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
offsetof(PyMethodDescrObject, vectorcall), /* tp_vectorcall_offset */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
0, /* tp_setattr */
|
0, /* tp_setattr */
|
||||||
0, /* tp_reserved */
|
0, /* tp_reserved */
|
||||||
|
@ -557,6 +558,7 @@ PyTypeObject PyMethodDescr_Type = {
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||||
|
_Py_TPFLAGS_HAVE_VECTORCALL |
|
||||||
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
||||||
0, /* tp_doc */
|
0, /* tp_doc */
|
||||||
descr_traverse, /* tp_traverse */
|
descr_traverse, /* tp_traverse */
|
||||||
|
@ -752,8 +754,10 @@ PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)
|
||||||
|
|
||||||
descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
|
descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
|
||||||
type, method->ml_name);
|
type, method->ml_name);
|
||||||
if (descr != NULL)
|
if (descr != NULL) {
|
||||||
descr->d_method = method;
|
descr->d_method = method;
|
||||||
|
descr->vectorcall = &_PyMethodDescr_FastCallKeywords;
|
||||||
|
}
|
||||||
return (PyObject *)descr;
|
return (PyObject *)descr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
|
||||||
op->func_defaults = NULL; /* No default arguments */
|
op->func_defaults = NULL; /* No default arguments */
|
||||||
op->func_kwdefaults = NULL; /* No keyword only defaults */
|
op->func_kwdefaults = NULL; /* No keyword only defaults */
|
||||||
op->func_closure = NULL;
|
op->func_closure = NULL;
|
||||||
|
op->vectorcall = _PyFunction_FastCallKeywords;
|
||||||
|
|
||||||
consts = ((PyCodeObject *)code)->co_consts;
|
consts = ((PyCodeObject *)code)->co_consts;
|
||||||
if (PyTuple_Size(consts) >= 1) {
|
if (PyTuple_Size(consts) >= 1) {
|
||||||
|
@ -649,7 +650,7 @@ PyTypeObject PyFunction_Type = {
|
||||||
sizeof(PyFunctionObject),
|
sizeof(PyFunctionObject),
|
||||||
0,
|
0,
|
||||||
(destructor)func_dealloc, /* tp_dealloc */
|
(destructor)func_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
offsetof(PyFunctionObject, vectorcall), /* tp_vectorcall_offset */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
0, /* tp_setattr */
|
0, /* tp_setattr */
|
||||||
0, /* tp_reserved */
|
0, /* tp_reserved */
|
||||||
|
@ -664,6 +665,7 @@ PyTypeObject PyFunction_Type = {
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||||
|
_Py_TPFLAGS_HAVE_VECTORCALL |
|
||||||
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
||||||
func_new__doc__, /* tp_doc */
|
func_new__doc__, /* tp_doc */
|
||||||
(traverseproc)func_traverse, /* tp_traverse */
|
(traverseproc)func_traverse, /* tp_traverse */
|
||||||
|
|
|
@ -46,6 +46,14 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
|
||||||
op->m_self = self;
|
op->m_self = self;
|
||||||
Py_XINCREF(module);
|
Py_XINCREF(module);
|
||||||
op->m_module = module;
|
op->m_module = module;
|
||||||
|
if (ml->ml_flags & METH_VARARGS) {
|
||||||
|
/* For METH_VARARGS functions, it's more efficient to use tp_call
|
||||||
|
* instead of vectorcall. */
|
||||||
|
op->vectorcall = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
op->vectorcall = &_PyCFunction_FastCallKeywords;
|
||||||
|
}
|
||||||
_PyObject_GC_TRACK(op);
|
_PyObject_GC_TRACK(op);
|
||||||
return (PyObject *)op;
|
return (PyObject *)op;
|
||||||
}
|
}
|
||||||
|
@ -264,7 +272,7 @@ PyTypeObject PyCFunction_Type = {
|
||||||
sizeof(PyCFunctionObject),
|
sizeof(PyCFunctionObject),
|
||||||
0,
|
0,
|
||||||
(destructor)meth_dealloc, /* tp_dealloc */
|
(destructor)meth_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
offsetof(PyCFunctionObject, vectorcall), /* tp_vectorcall_offset */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
0, /* tp_setattr */
|
0, /* tp_setattr */
|
||||||
0, /* tp_reserved */
|
0, /* tp_reserved */
|
||||||
|
@ -278,7 +286,8 @@ PyTypeObject PyCFunction_Type = {
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||||
|
_Py_TPFLAGS_HAVE_VECTORCALL, /* tp_flags */
|
||||||
0, /* tp_doc */
|
0, /* tp_doc */
|
||||||
(traverseproc)meth_traverse, /* tp_traverse */
|
(traverseproc)meth_traverse, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
|
|
|
@ -483,7 +483,7 @@ builtin_breakpoint(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_INCREF(hook);
|
Py_INCREF(hook);
|
||||||
PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords);
|
PyObject *retval = _PyObject_Vectorcall(hook, args, nargs, keywords);
|
||||||
Py_DECREF(hook);
|
Py_DECREF(hook);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -2231,7 +2231,7 @@ builtin_sorted(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(nargs >= 1);
|
assert(nargs >= 1);
|
||||||
v = _PyObject_FastCallKeywords(callable, args + 1, nargs - 1, kwnames);
|
v = _PyObject_Vectorcall(callable, args + 1, nargs - 1, kwnames);
|
||||||
Py_DECREF(callable);
|
Py_DECREF(callable);
|
||||||
if (v == NULL) {
|
if (v == NULL) {
|
||||||
Py_DECREF(newlist);
|
Py_DECREF(newlist);
|
||||||
|
|
|
@ -4806,6 +4806,40 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
||||||
x = call; \
|
x = call; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
trace_call_function(PyThreadState *tstate,
|
||||||
|
PyObject *func,
|
||||||
|
PyObject **args, Py_ssize_t nargs,
|
||||||
|
PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *x;
|
||||||
|
if (PyCFunction_Check(func)) {
|
||||||
|
C_TRACE(x, _PyCFunction_FastCallKeywords(func, args, nargs, kwnames));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
else if (Py_TYPE(func) == &PyMethodDescr_Type && nargs > 0) {
|
||||||
|
/* We need to create a temporary bound method as argument
|
||||||
|
for profiling.
|
||||||
|
|
||||||
|
If nargs == 0, then this cannot work because we have no
|
||||||
|
"self". In any case, the call itself would raise
|
||||||
|
TypeError (foo needs an argument), so we just skip
|
||||||
|
profiling. */
|
||||||
|
PyObject *self = args[0];
|
||||||
|
func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self));
|
||||||
|
if (func == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
C_TRACE(x, _PyCFunction_FastCallKeywords(func,
|
||||||
|
args+1, nargs-1,
|
||||||
|
kwnames));
|
||||||
|
Py_DECREF(func);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return _PyObject_Vectorcall(func, args, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
|
||||||
|
}
|
||||||
|
|
||||||
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
|
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
|
||||||
to reduce the stack consumption. */
|
to reduce the stack consumption. */
|
||||||
Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
|
Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
|
||||||
|
@ -4818,63 +4852,11 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
|
||||||
Py_ssize_t nargs = oparg - nkwargs;
|
Py_ssize_t nargs = oparg - nkwargs;
|
||||||
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
||||||
|
|
||||||
/* Always dispatch PyCFunction first, because these are
|
if (tstate->use_tracing) {
|
||||||
presumed to be the most frequent callable object.
|
x = trace_call_function(tstate, func, stack, nargs, kwnames);
|
||||||
*/
|
|
||||||
if (PyCFunction_Check(func)) {
|
|
||||||
C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
|
|
||||||
}
|
|
||||||
else if (Py_TYPE(func) == &PyMethodDescr_Type) {
|
|
||||||
if (nargs > 0 && tstate->use_tracing) {
|
|
||||||
/* We need to create a temporary bound method as argument
|
|
||||||
for profiling.
|
|
||||||
|
|
||||||
If nargs == 0, then this cannot work because we have no
|
|
||||||
"self". In any case, the call itself would raise
|
|
||||||
TypeError (foo needs an argument), so we just skip
|
|
||||||
profiling. */
|
|
||||||
PyObject *self = stack[0];
|
|
||||||
func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self));
|
|
||||||
if (func != NULL) {
|
|
||||||
C_TRACE(x, _PyCFunction_FastCallKeywords(func,
|
|
||||||
stack+1, nargs-1,
|
|
||||||
kwnames));
|
|
||||||
Py_DECREF(func);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
|
x = _PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
|
||||||
/* 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
|
|
||||||
FASTCALL. */
|
|
||||||
PyObject *self = PyMethod_GET_SELF(func);
|
|
||||||
Py_INCREF(self);
|
|
||||||
func = PyMethod_GET_FUNCTION(func);
|
|
||||||
Py_INCREF(func);
|
|
||||||
Py_SETREF(*pfunc, self);
|
|
||||||
nargs++;
|
|
||||||
stack--;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyFunction_Check(func)) {
|
|
||||||
x = _PyFunction_FastCallKeywords(func, stack, nargs, kwnames);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
|
|
||||||
}
|
|
||||||
Py_DECREF(func);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert((x != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
|
assert((x != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
|
||||||
|
|
|
@ -631,7 +631,7 @@ context_run(PyContext *self, PyObject *const *args,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *call_result = _PyObject_FastCallKeywords(
|
PyObject *call_result = _PyObject_Vectorcall(
|
||||||
args[0], args + 1, nargs - 1, kwnames);
|
args[0], args + 1, nargs - 1, kwnames);
|
||||||
|
|
||||||
if (PyContext_Exit((PyObject *)self)) {
|
if (PyContext_Exit((PyObject *)self)) {
|
||||||
|
|
|
@ -481,7 +481,7 @@ sys_breakpointhook(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyMem_RawFree(envar);
|
PyMem_RawFree(envar);
|
||||||
PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords);
|
PyObject *retval = _PyObject_Vectorcall(hook, args, nargs, keywords);
|
||||||
Py_DECREF(hook);
|
Py_DECREF(hook);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue