diff --git a/Include/abstract.h b/Include/abstract.h
index 3ca283a..f1999ef 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -171,22 +171,6 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict(
PyObject **values,
PyObject *kwnames);
-/* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames).
-
- Return a new stack which should be released by PyMem_Free(), or return
- args unchanged if kwargs is NULL or an empty dictionary.
-
- The stack uses borrowed references.
-
- The type of keyword keys is not checked, these checks should be done
- later (ex: _PyArg_ParseStack). */
-PyAPI_FUNC(PyObject **) _PyStack_UnpackDict(
- PyObject **args,
- Py_ssize_t nargs,
- PyObject *kwargs,
- PyObject **kwnames,
- PyObject *func);
-
/* 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
@@ -246,12 +230,28 @@ PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
PyObject *args,
PyObject *kwargs);
+PyAPI_FUNC(PyObject *) _PyObject_FastCall_Prepend(PyObject *func,
+ PyObject *obj,
+ PyObject **args,
+ Py_ssize_t nargs,
+ PyObject *kwnames);
+
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
PyObject *result,
const char *where);
#endif /* Py_LIMITED_API */
+#ifdef Py_BUILD_CORE
+PyAPI_FUNC(PyObject *) _Py_FastCall_FromArgs(
+ PyObject *self,
+ fastternaryfunc fastcall,
+ PyObject **args,
+ Py_ssize_t nargs,
+ PyObject *kwargs);
+#endif
+
+
/* Call a callable Python object 'callable', with arguments given by the
tuple 'args'. If no arguments are needed, then 'args' can be *NULL*.
diff --git a/Include/methodobject.h b/Include/methodobject.h
index 79fad82..1ad4c8c 100644
--- a/Include/methodobject.h
+++ b/Include/methodobject.h
@@ -16,8 +16,6 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type;
#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
-typedef PyObject *(*_PyCFunctionFast) (PyObject *self, PyObject **args,
- Py_ssize_t nargs, PyObject *kwnames);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
PyObject *);
typedef PyObject *(*PyNoArgsFunction)(PyObject *);
diff --git a/Include/modsupport.h b/Include/modsupport.h
index 8306ae7..611be0d 100644
--- a/Include/modsupport.h
+++ b/Include/modsupport.h
@@ -44,11 +44,15 @@ PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, ...);
PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *);
PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...);
+PyAPI_FUNC(int) PyArg_UnpackStack(PyObject **args, Py_ssize_t nargs,
+ const char *name,
+ Py_ssize_t min, Py_ssize_t max, ...);
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);
#endif
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw);
+PyAPI_FUNC(int) _PyArg_NoStackKeywords(const char *funcname, PyObject *kwnames);
PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args);
PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list);
diff --git a/Include/object.h b/Include/object.h
index 63e37b8..6809a6d 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -339,6 +339,8 @@ typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *);
typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *);
typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t);
+typedef PyObject* (*fastternaryfunc) (PyObject *self, PyObject **stack,
+ Py_ssize_t nargs, PyObject *kwnames);
#ifdef Py_LIMITED_API
typedef struct _typeobject PyTypeObject; /* opaque */
@@ -424,6 +426,8 @@ typedef struct _typeobject {
destructor tp_finalize;
+ fastternaryfunc tp_fastcall;
+
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index e6d8e50..1074658 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1092,7 +1092,7 @@ class SizeofTest(unittest.TestCase):
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
- fmt = 'P2n15Pl4Pn9Pn11PIP'
+ fmt = 'P2n15Pl4Pn9Pn11PIPP'
if hasattr(sys, 'getcounts'):
fmt += '3n2P'
s = vsize(fmt)
diff --git a/Modules/_operator.c b/Modules/_operator.c
index f5b8612..8e1fa58 100644
--- a/Modules/_operator.c
+++ b/Modules/_operator.c
@@ -455,24 +455,29 @@ itemgetter_traverse(itemgetterobject *ig, visitproc visit, void *arg)
}
static PyObject *
-itemgetter_call(itemgetterobject *ig, PyObject *args, PyObject *kw)
+itemgetter_call(itemgetterobject *ig, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
{
PyObject *obj, *result;
Py_ssize_t i, nitems=ig->nitems;
- if (kw != NULL && !_PyArg_NoKeywords("itemgetter", kw))
+ if (kwnames != NULL && !_PyArg_NoStackKeywords("itemgetter", kwnames)) {
return NULL;
- if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &obj))
+ }
+ if (!PyArg_UnpackStack(args, nargs, "itemgetter", 1, 1, &obj)) {
return NULL;
- if (nitems == 1)
+ }
+ if (nitems == 1) {
return PyObject_GetItem(obj, ig->item);
+ }
assert(PyTuple_Check(ig->item));
assert(PyTuple_GET_SIZE(ig->item) == nitems);
result = PyTuple_New(nitems);
- if (result == NULL)
+ if (result == NULL) {
return NULL;
+ }
for (i=0 ; i < nitems ; i++) {
PyObject *item, *val;
@@ -545,7 +550,7 @@ static PyTypeObject itemgetter_type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)itemgetter_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -570,6 +575,16 @@ static PyTypeObject itemgetter_type = {
0, /* tp_alloc */
itemgetter_new, /* tp_new */
0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)itemgetter_call, /* tp_fastcall */
};
@@ -744,24 +759,30 @@ dotted_getattr(PyObject *obj, PyObject *attr)
}
static PyObject *
-attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
+attrgetter_call(attrgetterobject *ag, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
{
PyObject *obj, *result;
Py_ssize_t i, nattrs=ag->nattrs;
- if (kw != NULL && !_PyArg_NoKeywords("attrgetter", kw))
+ if (kwnames != NULL && !_PyArg_NoStackKeywords("attrgetter", kwnames)) {
return NULL;
- if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &obj))
+ }
+ if (!PyArg_UnpackStack(args, nargs, "attrgetter", 1, 1, &obj)) {
return NULL;
- if (ag->nattrs == 1) /* ag->attr is always a tuple */
+ }
+ /* ag->attr is always a tuple */
+ if (ag->nattrs == 1) {
return dotted_getattr(obj, PyTuple_GET_ITEM(ag->attr, 0));
+ }
assert(PyTuple_Check(ag->attr));
assert(PyTuple_GET_SIZE(ag->attr) == nattrs);
result = PyTuple_New(nattrs);
- if (result == NULL)
+ if (result == NULL) {
return NULL;
+ }
for (i=0 ; i < nattrs ; i++) {
PyObject *attr, *val;
@@ -776,6 +797,7 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
return result;
}
+
static PyObject *
dotjoinattr(PyObject *attr, PyObject **attrsep)
{
@@ -888,7 +910,7 @@ static PyTypeObject attrgetter_type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)attrgetter_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -913,6 +935,16 @@ static PyTypeObject attrgetter_type = {
0, /* tp_alloc */
attrgetter_new, /* tp_new */
0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)attrgetter_call, /* tp_fastcall */
};
@@ -988,13 +1020,14 @@ methodcaller_traverse(methodcallerobject *mc, visitproc visit, void *arg)
}
static PyObject *
-methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw)
+methodcaller_call(methodcallerobject *mc, PyObject **args,
+ Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *method, *obj, *result;
- if (kw != NULL && !_PyArg_NoKeywords("methodcaller", kw))
+ if (kwnames != NULL && !_PyArg_NoStackKeywords("methodcaller", kwnames))
return NULL;
- if (!PyArg_UnpackTuple(args, "methodcaller", 1, 1, &obj))
+ if (!PyArg_UnpackStack(args, nargs, "methodcaller", 1, 1, &obj))
return NULL;
method = PyObject_GetAttr(obj, mc->name);
if (method == NULL)
@@ -1151,7 +1184,7 @@ static PyTypeObject methodcaller_type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)methodcaller_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -1176,6 +1209,16 @@ static PyTypeObject methodcaller_type = {
0, /* tp_alloc */
methodcaller_new, /* tp_new */
0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)methodcaller_call, /* tp_fastcall */
};
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 5726160..cc1fed3 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2305,11 +2305,17 @@ _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs,
else if (PyCFunction_Check(callable)) {
result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs);
}
+ /* FIXME: should we avoid tp_fastcall if tp_call is not fastcall_wrapper? */
+ else if (Py_TYPE(callable)->tp_fastcall) {
+ fastternaryfunc fastcall = Py_TYPE(callable)->tp_fastcall;
+
+ result = _Py_FastCall_FromArgs(callable, fastcall, args, nargs, kwargs);
+ }
else {
PyObject *tuple;
/* Slow-path: build a temporary tuple */
- call = callable->ob_type->tp_call;
+ call = Py_TYPE(callable)->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name);
@@ -2341,17 +2347,17 @@ _PyObject_Call_Prepend(PyObject *callable,
{
PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
PyObject **stack;
- Py_ssize_t argcount;
+ Py_ssize_t nargs;
PyObject *result;
assert(PyTuple_Check(args));
- argcount = PyTuple_GET_SIZE(args);
- if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
+ nargs = PyTuple_GET_SIZE(args);
+ if (nargs + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
stack = small_stack;
}
else {
- stack = PyMem_Malloc((argcount + 1) * sizeof(PyObject *));
+ stack = PyMem_Malloc((nargs + 1) * sizeof(PyObject *));
if (stack == NULL) {
PyErr_NoMemory();
return NULL;
@@ -2361,11 +2367,11 @@ _PyObject_Call_Prepend(PyObject *callable,
/* use borrowed references */
stack[0] = obj;
memcpy(&stack[1],
- &PyTuple_GET_ITEM(args, 0),
- argcount * sizeof(PyObject *));
+ &PyTuple_GET_ITEM(args, 0),
+ nargs * sizeof(PyObject *));
result = _PyObject_FastCallDict(callable,
- stack, argcount + 1,
+ stack, nargs + 1,
kwargs);
if (stack != small_stack) {
PyMem_Free(stack);
@@ -2374,6 +2380,47 @@ _PyObject_Call_Prepend(PyObject *callable,
}
PyObject *
+_PyObject_FastCall_Prepend(PyObject *func, PyObject *obj, PyObject **args,
+ Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
+ PyObject **stack;
+ PyObject *result;
+ Py_ssize_t alloc;
+
+ alloc = nargs + 1;
+ if (kwnames != NULL) {
+ alloc += PyTuple_GET_SIZE(kwnames);
+ }
+
+ if (alloc <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
+ stack = small_stack;
+ }
+ else {
+ stack = PyMem_Malloc(alloc * sizeof(PyObject *));
+ if (stack == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ }
+
+ /* use borrowed references */
+ stack[0] = obj;
+ memcpy(&stack[1], args, (alloc - 1) * sizeof(PyObject *));
+
+ assert(!PyErr_Occurred());
+ result = _PyObject_FastCallKeywords(func,
+ stack, nargs + 1,
+ kwnames);
+ if (stack != small_stack) {
+ PyMem_Free(stack);
+ }
+
+ result = _Py_CheckFunctionResult(func, result, NULL);
+ return result;
+}
+
+PyObject *
_PyStack_AsDict(PyObject **values, PyObject *kwnames)
{
Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames);
@@ -2398,9 +2445,9 @@ _PyStack_AsDict(PyObject **values, PyObject *kwnames)
return kwdict;
}
-PyObject **
+static int
_PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs,
- PyObject **p_kwnames, PyObject *func)
+ PyObject ***p_stack, PyObject **p_kwnames)
{
PyObject **stack, **kwstack;
Py_ssize_t nkwargs;
@@ -2412,29 +2459,34 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs,
assert(kwargs == NULL || PyDict_CheckExact(kwargs));
if (kwargs == NULL || (nkwargs = PyDict_GET_SIZE(kwargs)) == 0) {
+ /* Note: args can be NULL */
+ *p_stack = args;
*p_kwnames = NULL;
- return args;
+ return 0;
}
if ((size_t)nargs > PY_SSIZE_T_MAX / sizeof(stack[0]) - (size_t)nkwargs) {
PyErr_NoMemory();
- return NULL;
+ return -1;
}
stack = PyMem_Malloc((nargs + nkwargs) * sizeof(stack[0]));
if (stack == NULL) {
PyErr_NoMemory();
- return NULL;
+ return -1;
}
kwnames = PyTuple_New(nkwargs);
if (kwnames == NULL) {
PyMem_Free(stack);
- return NULL;
+ return -1;
}
- /* Copy position arguments (borrowed references) */
- memcpy(stack, args, nargs * sizeof(stack[0]));
+ /* args is NULL if nargs==0: don't call memcpy(stack, NULL, 0) */
+ if (nargs) {
+ /* Copy position arguments (borrowed references) */
+ memcpy(stack, args, nargs * sizeof(stack[0]));
+ }
kwstack = stack + nargs;
pos = i = 0;
@@ -2449,8 +2501,33 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs,
i++;
}
+ *p_stack = stack;
*p_kwnames = kwnames;
- return stack;
+ return 0;
+}
+
+PyObject *
+_Py_FastCall_FromArgs(PyObject *self, fastternaryfunc fastcall,
+ PyObject **args, Py_ssize_t nargs, PyObject *kwargs)
+{
+ PyObject **stack;
+ PyObject *kwnames;
+ PyObject *result;
+
+ assert(!PyErr_Occurred());
+
+ if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) {
+ return NULL;
+ }
+
+ result = fastcall(self, stack, nargs, kwnames);
+ if (stack != args) {
+ PyMem_Free(stack);
+ }
+ Py_XDECREF(kwnames);
+
+ result = _Py_CheckFunctionResult(self, result, NULL);
+ return result;
}
PyObject *
@@ -2465,11 +2542,27 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg
_PyArg_ParseStack(). */
if (PyFunction_Check(callable)) {
+ /* FIXME: should we use Py_EnterRecursiveCall() here? */
return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames);
}
if (PyCFunction_Check(callable)) {
return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames);
}
+ else if (Py_TYPE(callable)->tp_fastcall) {
+ fastternaryfunc fastcall = Py_TYPE(callable)->tp_fastcall;
+ PyObject *result;
+
+ if (Py_EnterRecursiveCall(" while calling a Python object")) {
+ return NULL;
+ }
+
+ result = fastcall(callable, stack, nargs, kwnames);
+ result = _Py_CheckFunctionResult(callable, result, NULL);
+
+ Py_LeaveRecursiveCall();
+
+ return result;
+ }
else {
/* Slow-path: build a temporary tuple for positional arguments and a
temporary dictionary for keyword arguments (if any) */
@@ -2487,7 +2580,7 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg
return NULL;
}
- call = callable->ob_type->tp_call;
+ call = Py_TYPE(callable)->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name);
@@ -2514,6 +2607,8 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg
Py_DECREF(argtuple);
Py_XDECREF(kwdict);
+ result = _Py_CheckFunctionResult(callable, result, NULL);
+
exit:
Py_LeaveRecursiveCall();
return result;
diff --git a/Objects/classobject.c b/Objects/classobject.c
index b0ed023..29706c0 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -302,7 +302,8 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg)
}
static PyObject *
-method_call(PyObject *method, PyObject *args, PyObject *kwargs)
+method_call(PyObject *method, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
{
PyObject *self, *func;
@@ -313,8 +314,7 @@ method_call(PyObject *method, PyObject *args, PyObject *kwargs)
}
func = PyMethod_GET_FUNCTION(method);
-
- return _PyObject_Call_Prepend(func, self, args, kwargs);
+ return _PyObject_FastCall_Prepend(func, self, args, nargs, kwnames);
}
static PyObject *
@@ -346,7 +346,7 @@ PyTypeObject PyMethod_Type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)method_hash, /* tp_hash */
- method_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
method_getattro, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
@@ -370,6 +370,17 @@ PyTypeObject PyMethod_Type = {
0, /* tp_init */
0, /* tp_alloc */
method_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)method_call, /* tp_fastcall */
};
/* Clear out the free list */
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index ed39891..4462a81 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -210,15 +210,13 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
}
static PyObject *
-methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
+methoddescr_call(PyMethodDescrObject *descr, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
{
- Py_ssize_t argc;
- PyObject *self, *func, *result, **stack;
+ PyObject *self, *func, *result;
/* Make sure that the first argument is acceptable as 'self' */
- assert(PyTuple_Check(args));
- argc = PyTuple_GET_SIZE(args);
- if (argc < 1) {
+ if (nargs < 1) {
PyErr_Format(PyExc_TypeError,
"descriptor '%V' of '%.100s' "
"object needs an argument",
@@ -226,7 +224,7 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
PyDescr_TYPE(descr)->tp_name);
return NULL;
}
- self = PyTuple_GET_ITEM(args, 0);
+ self = args[0];
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
(PyObject *)PyDescr_TYPE(descr))) {
PyErr_Format(PyExc_TypeError,
@@ -242,8 +240,7 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
func = PyCFunction_NewEx(descr->d_method, self, NULL);
if (func == NULL)
return NULL;
- stack = &PyTuple_GET_ITEM(args, 1);
- result = _PyObject_FastCallDict(func, stack, argc - 1, kwds);
+ result = _PyCFunction_FastCallKeywords(func, args + 1, nargs - 1, kwnames);
Py_DECREF(func);
return result;
}
@@ -297,15 +294,13 @@ classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args,
}
static PyObject *
-wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
+wrapperdescr_call(PyMethodDescrObject *descr, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
{
- Py_ssize_t argc;
- PyObject *self, *func, *result, **stack;
+ PyObject *self, *func, *result;
/* Make sure that the first argument is acceptable as 'self' */
- assert(PyTuple_Check(args));
- argc = PyTuple_GET_SIZE(args);
- if (argc < 1) {
+ if (nargs < 1) {
PyErr_Format(PyExc_TypeError,
"descriptor '%V' of '%.100s' "
"object needs an argument",
@@ -313,7 +308,7 @@ wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
PyDescr_TYPE(descr)->tp_name);
return NULL;
}
- self = PyTuple_GET_ITEM(args, 0);
+ self = args[0];
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
(PyObject *)PyDescr_TYPE(descr))) {
PyErr_Format(PyExc_TypeError,
@@ -330,8 +325,7 @@ wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
if (func == NULL)
return NULL;
- stack = &PyTuple_GET_ITEM(args, 1);
- result = _PyObject_FastCallDict(func, stack, argc - 1, kwds);
+ result = _PyObject_FastCallKeywords(func, args + 1, nargs - 1, kwnames);
Py_DECREF(func);
return result;
}
@@ -491,7 +485,7 @@ PyTypeObject PyMethodDescr_Type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)methoddescr_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -511,6 +505,21 @@ PyTypeObject PyMethodDescr_Type = {
0, /* tp_dict */
(descrgetfunc)method_get, /* tp_descr_get */
0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)methoddescr_call, /* tp_fastcall */
};
/* This is for METH_CLASS in C, not for "f = classmethod(f)" in Python! */
@@ -640,7 +649,7 @@ PyTypeObject PyWrapperDescr_Type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)wrapperdescr_call, /* tp_call */
+ 0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -660,6 +669,21 @@ PyTypeObject PyWrapperDescr_Type = {
0, /* tp_dict */
(descrgetfunc)wrapperdescr_get, /* tp_descr_get */
0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)wrapperdescr_call, /* tp_fastcall */
};
static PyDescrObject *
@@ -1360,7 +1384,6 @@ static PyObject *
property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
static PyObject * volatile cached_args = NULL;
- PyObject *args;
PyObject *ret;
propertyobject *gs = (propertyobject *)self;
@@ -1372,27 +1395,37 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
return NULL;
}
- args = cached_args;
- cached_args = NULL;
- if (!args) {
- args = PyTuple_New(1);
- if (!args)
- return NULL;
- _PyObject_GC_UNTRACK(args);
- }
- Py_INCREF(obj);
- PyTuple_SET_ITEM(args, 0, obj);
- ret = PyObject_Call(gs->prop_get, args, NULL);
- if (cached_args == NULL && Py_REFCNT(args) == 1) {
- assert(Py_SIZE(args) == 1);
- assert(PyTuple_GET_ITEM(args, 0) == obj);
- cached_args = args;
- Py_DECREF(obj);
+
+ if (Py_TYPE(gs->prop_get)->tp_fastcall) {
+ PyObject *args[1] = {obj};
+
+ ret = _PyObject_FastCall(gs->prop_get, args, 1);
}
else {
- assert(Py_REFCNT(args) >= 1);
- _PyObject_GC_TRACK(args);
- Py_DECREF(args);
+ PyObject *args;
+
+ args = cached_args;
+ cached_args = NULL;
+ if (!args) {
+ args = PyTuple_New(1);
+ if (!args)
+ return NULL;
+ _PyObject_GC_UNTRACK(args);
+ }
+ Py_INCREF(obj);
+ PyTuple_SET_ITEM(args, 0, obj);
+ ret = PyObject_Call(gs->prop_get, args, NULL);
+ if (cached_args == NULL && Py_REFCNT(args) == 1) {
+ assert(Py_SIZE(args) == 1);
+ assert(PyTuple_GET_ITEM(args, 0) == obj);
+ cached_args = args;
+ Py_DECREF(obj);
+ }
+ else {
+ assert(Py_REFCNT(args) >= 1);
+ _PyObject_GC_TRACK(args);
+ Py_DECREF(args);
+ }
}
return ret;
}
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index a3af4b3..093a5e4 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -562,6 +562,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
return 0;
}
+#if 0
static PyObject *
function_call(PyObject *func, PyObject *args, PyObject *kwargs)
{
@@ -572,6 +573,7 @@ function_call(PyObject *func, PyObject *args, PyObject *kwargs)
nargs = PyTuple_GET_SIZE(args);
return _PyFunction_FastCallDict(func, stack, nargs, kwargs);
}
+#endif
/* Bind a function to an object */
static PyObject *
@@ -599,7 +601,12 @@ PyTypeObject PyFunction_Type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
+#if 0
function_call, /* tp_call */
+#else
+ /* FIXME: revert this change, 0 is used to test fastcall_wrapper() */
+ 0, /* tp_call */
+#endif
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
@@ -623,6 +630,17 @@ PyTypeObject PyFunction_Type = {
0, /* tp_init */
0, /* tp_alloc */
func_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ _PyFunction_FastCallKeywords, /* tp_fastcall */
};
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 14750b6..b1ed7d6 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -240,20 +240,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
case METH_FASTCALL:
{
- PyObject **stack;
- PyObject *kwnames;
- _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;
-
- stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj);
- if (stack == NULL) {
- return NULL;
- }
-
- result = (*fastmeth) (self, stack, nargs, kwnames);
- if (stack != args) {
- PyMem_Free(stack);
- }
- Py_XDECREF(kwnames);
+ result = _Py_FastCall_FromArgs(self, (fastternaryfunc)meth,
+ args, nargs, kwargs);
break;
}
@@ -520,6 +508,23 @@ PyTypeObject PyCFunction_Type = {
meth_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ _PyCFunction_FastCallKeywords, /* tp_fastcall */
};
/* Clear out the free list */
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 05fc7b8..bbc07da 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -4671,6 +4671,17 @@ overrides_hash(PyTypeObject *type)
return 0;
}
+/* tp_call slot which calls tp_fastcall */
+static PyObject *
+fastcall_wrapper(PyObject *callable, PyObject *args_tuple, PyObject *kwargs)
+{
+ return _Py_FastCall_FromArgs(callable,
+ Py_TYPE(callable)->tp_fastcall,
+ &PyTuple_GET_ITEM(args_tuple, 0),
+ PyTuple_GET_SIZE(args_tuple),
+ kwargs);
+}
+
static void
inherit_slots(PyTypeObject *type, PyTypeObject *base)
{
@@ -4794,7 +4805,17 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
/* tp_reserved is ignored */
COPYSLOT(tp_repr);
/* tp_hash see tp_richcompare */
- COPYSLOT(tp_call);
+
+ if (type->tp_call != fastcall_wrapper) {
+ /* don't inherit tp_fastcall if tp_call is defined but
+ not tp_fastcall */
+ if (!type->tp_call && !type->tp_fastcall) {
+ COPYSLOT(tp_fastcall);
+ }
+
+ COPYSLOT(tp_call);
+ }
+
COPYSLOT(tp_str);
{
/* Copy comparison-related slots only when
@@ -4923,6 +4944,14 @@ PyType_Ready(PyTypeObject *type)
type->tp_dict = dict;
}
+ if (type->tp_fastcall && !type->tp_call) {
+ /* tp_fastcall defined, but not tp_call: tp_call will use a wrapper
+ calling tp_fastcall. We need to define tp_call before calling
+ add_operators(), otherwise the __call__ descriptor is not
+ defined. */
+ type->tp_call = fastcall_wrapper;
+ }
+
/* Add type-specific descriptors to tp_dict */
if (add_operators(type) < 0)
goto error;
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index ab6b235..8066218 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -461,7 +461,15 @@ proxy_checkref(PyWeakReference *proxy)
WRAP_BINARY(proxy_getattr, PyObject_GetAttr)
WRAP_UNARY(proxy_str, PyObject_Str)
-WRAP_TERNARY(proxy_call, PyEval_CallObjectWithKeywords)
+
+static PyObject *
+proxy_call(PyObject *proxy, PyObject **args, Py_ssize_t nargs,
+ PyObject *kwnames)
+{
+ UNWRAP(proxy);
+ return _PyObject_FastCallKeywords(proxy, args, nargs, kwnames);
+}
+
static PyObject *
proxy_repr(PyWeakReference *proxy)
@@ -711,7 +719,7 @@ _PyWeakref_CallableProxyType = {
&proxy_as_sequence, /* tp_as_sequence */
&proxy_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
- proxy_call, /* tp_call */
+ 0, /* tp_call */
proxy_str, /* tp_str */
proxy_getattr, /* tp_getattro */
(setattrofunc)proxy_setattr, /* tp_setattro */
@@ -724,6 +732,28 @@ _PyWeakref_CallableProxyType = {
0, /* tp_weaklistoffset */
(getiterfunc)proxy_iter, /* tp_iter */
(iternextfunc)proxy_iternext, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+ (fastternaryfunc)proxy_call, /* tp_fastcall */
};
diff --git a/Python/getargs.c b/Python/getargs.c
index 49888d1..fdda271 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -2347,21 +2347,16 @@ err:
}
-int
-PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...)
+static int
+PyArg_UnpackStack_impl(PyObject **args, Py_ssize_t l, const char *name,
+ Py_ssize_t min, Py_ssize_t max, va_list vargs)
{
- Py_ssize_t i, l;
+ Py_ssize_t i;
PyObject **o;
- va_list vargs;
assert(min >= 0);
assert(min <= max);
- if (!PyTuple_Check(args)) {
- PyErr_SetString(PyExc_SystemError,
- "PyArg_UnpackTuple() argument list is not a tuple");
- return 0;
- }
- l = PyTuple_GET_SIZE(args);
+
if (l < min) {
if (name != NULL)
PyErr_Format(
@@ -2376,8 +2371,11 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m
(min == max ? "" : "at least "), min, l);
return 0;
}
- if (l == 0)
+
+ if (l == 0) {
return 1;
+ }
+
if (l > max) {
if (name != NULL)
PyErr_Format(
@@ -2393,17 +2391,54 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m
return 0;
}
+ for (i = 0; i < l; i++) {
+ o = va_arg(vargs, PyObject **);
+ *o = args[i];
+ }
+ return 1;
+}
+
+int
+PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...)
+{
+ PyObject **stack;
+ Py_ssize_t nargs;
+ int retval;
+ va_list vargs;
+
+ if (!PyTuple_Check(args)) {
+ PyErr_SetString(PyExc_SystemError,
+ "PyArg_UnpackTuple() argument list is not a tuple");
+ return 0;
+ }
+ stack = &PyTuple_GET_ITEM(args, 0);
+ nargs = PyTuple_GET_SIZE(args);
+
#ifdef HAVE_STDARG_PROTOTYPES
va_start(vargs, max);
#else
va_start(vargs);
#endif
- for (i = 0; i < l; i++) {
- o = va_arg(vargs, PyObject **);
- *o = PyTuple_GET_ITEM(args, i);
- }
+ retval = PyArg_UnpackStack_impl(stack, nargs, name, min, max, vargs);
va_end(vargs);
- return 1;
+ return retval;
+}
+
+int
+PyArg_UnpackStack(PyObject **args, Py_ssize_t nargs, const char *name,
+ Py_ssize_t min, Py_ssize_t max, ...)
+{
+ int retval;
+ va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, max);
+#else
+ va_start(vargs);
+#endif
+ retval = PyArg_UnpackStack_impl(args, nargs, name, min, max, vargs);
+ va_end(vargs);
+ return retval;
}
@@ -2415,8 +2450,9 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m
int
_PyArg_NoKeywords(const char *funcname, PyObject *kw)
{
- if (kw == NULL)
+ if (kw == NULL) {
return 1;
+ }
if (!PyDict_CheckExact(kw)) {
PyErr_BadInternalCall();
return 0;
@@ -2429,6 +2465,25 @@ _PyArg_NoKeywords(const char *funcname, PyObject *kw)
return 0;
}
+int
+_PyArg_NoStackKeywords(const char *funcname, PyObject *kwnames)
+{
+ if (kwnames == NULL) {
+ return 1;
+ }
+ if (!PyTuple_CheckExact(kwnames)) {
+ PyErr_BadInternalCall();
+ return 0;
+ }
+ if (PyTuple_GET_SIZE(kwnames) == 0) {
+ return 1;
+ }
+
+ PyErr_Format(PyExc_TypeError, "%s does not take keyword arguments",
+ funcname);
+ return 0;
+}
+
int
_PyArg_NoPositional(const char *funcname, PyObject *args)