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_self; /* The instance it is bound to */
|
||||
PyObject *im_weakreflist; /* List of weak references */
|
||||
vectorcallfunc vectorcall;
|
||||
} PyMethodObject;
|
||||
|
||||
PyAPI_DATA(PyTypeObject) PyMethod_Type;
|
||||
|
|
|
@ -47,7 +47,7 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
|
|||
/* 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
|
||||
_PyObject_FastCall() family.
|
||||
_PyObject_Vectorcall() family.
|
||||
|
||||
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
|
||||
|
@ -56,50 +56,103 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
|
|||
#define _PY_FASTCALL_SMALL_STACK 5
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Call the callable object 'callable' with the "fast call" calling convention:
|
||||
args is a C array for positional arguments (nargs is the number of
|
||||
positional arguments), kwargs is a dictionary for keyword arguments.
|
||||
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
|
||||
PyObject *result,
|
||||
const char *where);
|
||||
|
||||
If nargs is equal to zero, args can be NULL. kwargs can be NULL.
|
||||
nargs must be greater or equal to zero.
|
||||
/* === Vectorcall protocol (PEP 590) ============================= */
|
||||
|
||||
/* 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
|
||||
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(
|
||||
PyObject *callable,
|
||||
PyObject *const *args,
|
||||
Py_ssize_t nargs,
|
||||
size_t nargsf,
|
||||
PyObject *kwargs);
|
||||
|
||||
/* Call the callable object 'callable' with the "fast call" calling convention:
|
||||
args is a C array for positional arguments followed by values of
|
||||
keyword arguments. Keys of keyword arguments are stored as a tuple
|
||||
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.
|
||||
/* Call "callable" (which must support vectorcall) with positional arguments
|
||||
"tuple" and keyword arguments "dict". "dict" may also be NULL */
|
||||
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
|
||||
|
||||
kwnames must only contains str strings, no subclass, and all keys must
|
||||
be unique.
|
||||
/* Same as _PyObject_Vectorcall except without keyword arguments */
|
||||
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
|
||||
NULL or its size is zero), args can be NULL.
|
||||
|
||||
Return the result on success. Raise an exception and return NULL on
|
||||
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)
|
||||
/* Call a callable without any arguments */
|
||||
static inline PyObject *
|
||||
_PyObject_CallNoArg(PyObject *func) {
|
||||
return _PyObject_Vectorcall(func, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
|
||||
PyObject *callable,
|
||||
|
@ -113,10 +166,6 @@ PyAPI_FUNC(PyObject *) _PyObject_FastCall_Prepend(
|
|||
PyObject *const *args,
|
||||
Py_ssize_t nargs);
|
||||
|
||||
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
|
||||
PyObject *result,
|
||||
const char *where);
|
||||
|
||||
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
|
||||
as the method name. */
|
||||
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
|
||||
|
|
|
@ -55,6 +55,9 @@ typedef struct bufferinfo {
|
|||
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
|
||||
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
|
||||
|
||||
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames);
|
||||
|
||||
/* Maximum number of dimensions */
|
||||
#define PyBUF_MAX_NDIM 64
|
||||
|
||||
|
@ -167,12 +170,9 @@ typedef struct {
|
|||
releasebufferproc bf_releasebuffer;
|
||||
} PyBufferProcs;
|
||||
|
||||
/* We can't provide a full compile-time check that limited-API
|
||||
users won't implement tp_print. However, not defining printfunc
|
||||
and making tp_print of a different function pointer type
|
||||
if Py_LIMITED_API is set should at least cause a warning
|
||||
in most cases. */
|
||||
typedef int (*printfunc)(PyObject *, FILE *, int);
|
||||
/* Allow printfunc in the tp_vectorcall_offset slot for
|
||||
* backwards-compatibility */
|
||||
typedef Py_ssize_t printfunc;
|
||||
|
||||
typedef struct _typeobject {
|
||||
PyObject_VAR_HEAD
|
||||
|
@ -182,7 +182,7 @@ typedef struct _typeobject {
|
|||
/* Methods to implement standard operations */
|
||||
|
||||
destructor tp_dealloc;
|
||||
printfunc tp_print;
|
||||
Py_ssize_t tp_vectorcall_offset;
|
||||
getattrfunc tp_getattr;
|
||||
setattrfunc tp_setattr;
|
||||
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
|
||||
|
@ -254,6 +254,7 @@ typedef struct _typeobject {
|
|||
unsigned int tp_version_tag;
|
||||
|
||||
destructor tp_finalize;
|
||||
vectorcallfunc tp_vectorcall;
|
||||
|
||||
#ifdef COUNT_ALLOCS
|
||||
/* these must be last and never explicitly initialized */
|
||||
|
|
|
@ -53,6 +53,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
PyDescr_COMMON;
|
||||
PyMethodDef *d_method;
|
||||
vectorcallfunc vectorcall;
|
||||
} PyMethodDescrObject;
|
||||
|
||||
typedef struct {
|
||||
|
@ -92,7 +93,7 @@ PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *,
|
|||
#ifndef Py_LIMITED_API
|
||||
|
||||
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 *,
|
||||
struct wrapperbase *, void *);
|
||||
#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_annotations; /* Annotations, a dict or NULL */
|
||||
PyObject *func_qualname; /* The qualified name */
|
||||
vectorcallfunc vectorcall;
|
||||
|
||||
/* Invariant:
|
||||
* 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(
|
||||
PyObject *func,
|
||||
PyObject *const *stack,
|
||||
Py_ssize_t nargs,
|
||||
size_t nargsf,
|
||||
PyObject *kwnames);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func,
|
|||
|
||||
PyAPI_FUNC(PyObject *) _PyCFunction_FastCallKeywords(PyObject *func,
|
||||
PyObject *const *stack,
|
||||
Py_ssize_t nargs,
|
||||
size_t nargsf,
|
||||
PyObject *kwnames);
|
||||
#endif
|
||||
|
||||
|
@ -105,6 +105,7 @@ typedef struct {
|
|||
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_weakreflist; /* List of weak references */
|
||||
vectorcallfunc vectorcall;
|
||||
} PyCFunctionObject;
|
||||
|
||||
PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict(
|
||||
|
|
|
@ -291,6 +291,11 @@ given type object has a specified feature.
|
|||
/* Set if the type allows subclassing */
|
||||
#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 */
|
||||
#define Py_TPFLAGS_READY (1UL << 12)
|
||||
|
||||
|
|
|
@ -402,7 +402,7 @@ class FastCallTests(unittest.TestCase):
|
|||
result = _testcapi.pyobject_fastcall(func, None)
|
||||
self.check_result(result, expected)
|
||||
|
||||
def test_fastcall_dict(self):
|
||||
def test_vectorcall_dict(self):
|
||||
# Test _PyObject_FastCallDict()
|
||||
|
||||
for func, args, expected in self.CALLS_POSARGS:
|
||||
|
@ -429,33 +429,33 @@ class FastCallTests(unittest.TestCase):
|
|||
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
|
||||
self.check_result(result, expected)
|
||||
|
||||
def test_fastcall_keywords(self):
|
||||
# Test _PyObject_FastCallKeywords()
|
||||
def test_vectorcall(self):
|
||||
# Test _PyObject_Vectorcall()
|
||||
|
||||
for func, args, expected in self.CALLS_POSARGS:
|
||||
with self.subTest(func=func, args=args):
|
||||
# kwnames=NULL
|
||||
result = _testcapi.pyobject_fastcallkeywords(func, args, None)
|
||||
result = _testcapi.pyobject_vectorcall(func, args, None)
|
||||
self.check_result(result, expected)
|
||||
|
||||
# kwnames=()
|
||||
result = _testcapi.pyobject_fastcallkeywords(func, args, ())
|
||||
result = _testcapi.pyobject_vectorcall(func, args, ())
|
||||
self.check_result(result, expected)
|
||||
|
||||
if not args:
|
||||
# kwnames=NULL
|
||||
result = _testcapi.pyobject_fastcallkeywords(func, None, None)
|
||||
result = _testcapi.pyobject_vectorcall(func, None, None)
|
||||
self.check_result(result, expected)
|
||||
|
||||
# kwnames=()
|
||||
result = _testcapi.pyobject_fastcallkeywords(func, None, ())
|
||||
result = _testcapi.pyobject_vectorcall(func, None, ())
|
||||
self.check_result(result, expected)
|
||||
|
||||
for func, args, kwargs, expected in self.CALLS_KWARGS:
|
||||
with self.subTest(func=func, args=args, kwargs=kwargs):
|
||||
kwnames = tuple(kwargs.keys())
|
||||
args = args + tuple(kwargs.values())
|
||||
result = _testcapi.pyobject_fastcallkeywords(func, args, kwnames)
|
||||
result = _testcapi.pyobject_vectorcall(func, args, kwnames)
|
||||
self.check_result(result, expected)
|
||||
|
||||
def test_fastcall_clearing_dict(self):
|
||||
|
|
|
@ -34,6 +34,11 @@ def testfunction(self):
|
|||
"""some doc"""
|
||||
return self
|
||||
|
||||
def testfunction_kw(self, *, kw):
|
||||
"""some doc"""
|
||||
return self
|
||||
|
||||
|
||||
class InstanceMethod:
|
||||
id = _testcapi.instancemethod(id)
|
||||
testfunction = _testcapi.instancemethod(testfunction)
|
||||
|
@ -479,6 +484,48 @@ class TestPEP590(unittest.TestCase):
|
|||
pass
|
||||
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):
|
||||
|
||||
|
|
|
@ -1064,7 +1064,7 @@ class SizeofTest(unittest.TestCase):
|
|||
# buffer
|
||||
# XXX
|
||||
# builtin_function_or_method
|
||||
check(len, size('4P')) # XXX check layout
|
||||
check(len, size('5P'))
|
||||
# bytearray
|
||||
samples = [b'', b'u'*100000]
|
||||
for sample in samples:
|
||||
|
@ -1095,7 +1095,7 @@ class SizeofTest(unittest.TestCase):
|
|||
# complex
|
||||
check(complex(0,1), size('2d'))
|
||||
# method_descriptor (descriptor object)
|
||||
check(str.lower, size('3PP'))
|
||||
check(str.lower, size('3PPP'))
|
||||
# classmethod_descriptor (descriptor object)
|
||||
# XXX
|
||||
# member_descriptor (descriptor object)
|
||||
|
@ -1164,7 +1164,7 @@ class SizeofTest(unittest.TestCase):
|
|||
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
||||
# function
|
||||
def func(): pass
|
||||
check(func, size('12P'))
|
||||
check(func, size('13P'))
|
||||
class c():
|
||||
@staticmethod
|
||||
def foo():
|
||||
|
@ -1259,7 +1259,7 @@ class SizeofTest(unittest.TestCase):
|
|||
check((1,2,3), vsize('') + 3*self.P)
|
||||
# type
|
||||
# static type: PyTypeObject
|
||||
fmt = 'P2n15Pl4Pn9Pn11PIP'
|
||||
fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
|
||||
if hasattr(sys, 'getcounts'):
|
||||
fmt += '3n2P'
|
||||
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;
|
||||
|
||||
handle = _PyObject_FastCallKeywords(
|
||||
callable, stack, nargs, context_kwname);
|
||||
handle = _PyObject_Vectorcall(callable, stack, nargs, context_kwname);
|
||||
Py_DECREF(callable);
|
||||
}
|
||||
|
||||
|
@ -2805,8 +2804,7 @@ set_exception:
|
|||
PyObject *stack[2];
|
||||
stack[0] = wrapper;
|
||||
stack[1] = (PyObject *)task->task_context;
|
||||
res = _PyObject_FastCallKeywords(
|
||||
add_cb, stack, 1, context_kwname);
|
||||
res = _PyObject_Vectorcall(add_cb, stack, 1, context_kwname);
|
||||
Py_DECREF(add_cb);
|
||||
Py_DECREF(wrapper);
|
||||
if (res == NULL) {
|
||||
|
|
|
@ -4713,7 +4713,7 @@ test_pyobject_fastcalldict(PyObject *self, PyObject *args)
|
|||
|
||||
|
||||
static PyObject *
|
||||
test_pyobject_fastcallkeywords(PyObject *self, PyObject *args)
|
||||
test_pyobject_vectorcall(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *func, *func_args, *kwnames = NULL;
|
||||
PyObject **stack;
|
||||
|
@ -4742,7 +4742,31 @@ test_pyobject_fastcallkeywords(PyObject *self, PyObject *args)
|
|||
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
|
||||
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},
|
||||
{"pyobject_fastcall", test_pyobject_fastcall, 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},
|
||||
#ifdef W_STOPCODE
|
||||
{"W_STOPCODE", py_w_stopcode, METH_VARARGS},
|
||||
|
|
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,45 @@ PyMethod_Self(PyObject *im)
|
|||
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
|
||||
instancename.methodname. ClassName.methodname returns an ordinary
|
||||
function.
|
||||
|
@ -69,6 +108,7 @@ PyMethod_New(PyObject *func, PyObject *self)
|
|||
im->im_func = func;
|
||||
Py_XINCREF(self);
|
||||
im->im_self = self;
|
||||
im->vectorcall = method_vectorcall;
|
||||
_PyObject_GC_TRACK(im);
|
||||
return (PyObject *)im;
|
||||
}
|
||||
|
@ -309,7 +349,7 @@ PyTypeObject PyMethod_Type = {
|
|||
sizeof(PyMethodObject),
|
||||
0,
|
||||
(destructor)method_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
offsetof(PyMethodObject, vectorcall), /* tp_vectorcall_offset */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
|
@ -323,7 +363,8 @@ PyTypeObject PyMethod_Type = {
|
|||
method_getattro, /* tp_getattro */
|
||||
PyObject_GenericSetAttr, /* tp_setattro */
|
||||
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 */
|
||||
(traverseproc)method_traverse, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
|
|
|
@ -265,13 +265,14 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
|
|||
// same to methoddescr_call(), but use FASTCALL convention.
|
||||
PyObject *
|
||||
_PyMethodDescr_FastCallKeywords(PyObject *descrobj,
|
||||
PyObject *const *args, Py_ssize_t nargs,
|
||||
PyObject *const *args, size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
assert(Py_TYPE(descrobj) == &PyMethodDescr_Type);
|
||||
PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj;
|
||||
PyObject *self, *result;
|
||||
|
||||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
/* Make sure that the first argument is acceptable as 'self' */
|
||||
if (nargs < 1) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
|
@ -542,7 +543,7 @@ PyTypeObject PyMethodDescr_Type = {
|
|||
sizeof(PyMethodDescrObject),
|
||||
0,
|
||||
(destructor)descr_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
offsetof(PyMethodDescrObject, vectorcall), /* tp_vectorcall_offset */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
|
@ -557,6 +558,7 @@ PyTypeObject PyMethodDescr_Type = {
|
|||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||
_Py_TPFLAGS_HAVE_VECTORCALL |
|
||||
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
descr_traverse, /* tp_traverse */
|
||||
|
@ -752,8 +754,10 @@ PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)
|
|||
|
||||
descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
|
||||
type, method->ml_name);
|
||||
if (descr != NULL)
|
||||
if (descr != NULL) {
|
||||
descr->d_method = method;
|
||||
descr->vectorcall = &_PyMethodDescr_FastCallKeywords;
|
||||
}
|
||||
return (PyObject *)descr;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
|
|||
op->func_defaults = NULL; /* No default arguments */
|
||||
op->func_kwdefaults = NULL; /* No keyword only defaults */
|
||||
op->func_closure = NULL;
|
||||
op->vectorcall = _PyFunction_FastCallKeywords;
|
||||
|
||||
consts = ((PyCodeObject *)code)->co_consts;
|
||||
if (PyTuple_Size(consts) >= 1) {
|
||||
|
@ -649,7 +650,7 @@ PyTypeObject PyFunction_Type = {
|
|||
sizeof(PyFunctionObject),
|
||||
0,
|
||||
(destructor)func_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
offsetof(PyFunctionObject, vectorcall), /* tp_vectorcall_offset */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
|
@ -664,6 +665,7 @@ PyTypeObject PyFunction_Type = {
|
|||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||
_Py_TPFLAGS_HAVE_VECTORCALL |
|
||||
Py_TPFLAGS_METHOD_DESCRIPTOR, /* tp_flags */
|
||||
func_new__doc__, /* tp_doc */
|
||||
(traverseproc)func_traverse, /* tp_traverse */
|
||||
|
|
|
@ -46,6 +46,14 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
|
|||
op->m_self = self;
|
||||
Py_XINCREF(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);
|
||||
return (PyObject *)op;
|
||||
}
|
||||
|
@ -264,7 +272,7 @@ PyTypeObject PyCFunction_Type = {
|
|||
sizeof(PyCFunctionObject),
|
||||
0,
|
||||
(destructor)meth_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
offsetof(PyCFunctionObject, vectorcall), /* tp_vectorcall_offset */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
|
@ -278,7 +286,8 @@ PyTypeObject PyCFunction_Type = {
|
|||
PyObject_GenericGetAttr, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
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 */
|
||||
(traverseproc)meth_traverse, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
|
|
|
@ -483,7 +483,7 @@ builtin_breakpoint(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb
|
|||
return NULL;
|
||||
}
|
||||
Py_INCREF(hook);
|
||||
PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords);
|
||||
PyObject *retval = _PyObject_Vectorcall(hook, args, nargs, keywords);
|
||||
Py_DECREF(hook);
|
||||
return retval;
|
||||
}
|
||||
|
@ -2231,7 +2231,7 @@ builtin_sorted(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
|
|||
}
|
||||
|
||||
assert(nargs >= 1);
|
||||
v = _PyObject_FastCallKeywords(callable, args + 1, nargs - 1, kwnames);
|
||||
v = _PyObject_Vectorcall(callable, args + 1, nargs - 1, kwnames);
|
||||
Py_DECREF(callable);
|
||||
if (v == NULL) {
|
||||
Py_DECREF(newlist);
|
||||
|
|
|
@ -4806,6 +4806,40 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
|||
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()
|
||||
to reduce the stack consumption. */
|
||||
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;
|
||||
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
||||
|
||||
/* Always dispatch PyCFunction first, because these are
|
||||
presumed to be the most frequent callable object.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
if (tstate->use_tracing) {
|
||||
x = trace_call_function(tstate, 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
|
||||
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);
|
||||
x = _PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
|
||||
}
|
||||
|
||||
assert((x != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
|
||||
|
|
|
@ -631,7 +631,7 @@ context_run(PyContext *self, PyObject *const *args,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *call_result = _PyObject_FastCallKeywords(
|
||||
PyObject *call_result = _PyObject_Vectorcall(
|
||||
args[0], args + 1, nargs - 1, kwnames);
|
||||
|
||||
if (PyContext_Exit((PyObject *)self)) {
|
||||
|
|
|
@ -481,7 +481,7 @@ sys_breakpointhook(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb
|
|||
return NULL;
|
||||
}
|
||||
PyMem_RawFree(envar);
|
||||
PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords);
|
||||
PyObject *retval = _PyObject_Vectorcall(hook, args, nargs, keywords);
|
||||
Py_DECREF(hook);
|
||||
return retval;
|
||||
|
||||
|
|
Loading…
Reference in New Issue