summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/object.rst22
-rw-r--r--Include/cpython/abstract.h16
-rw-r--r--Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst1
-rw-r--r--Modules/_abc.c19
-rw-r--r--Objects/call.c32
-rw-r--r--Objects/descrobject.c21
-rw-r--r--Python/sysmodule.c27
7 files changed, 105 insertions, 33 deletions
diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 13f13b3..a84235b 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -395,6 +395,9 @@ Object Protocol
argument 1 (not 0) in the allocated vector.
The callee must restore the value of ``args[-1]`` before returning.
+ For :c:func:`_PyObject_VectorcallMethod`, this flag means instead that
+ ``args[0]`` may be changed.
+
Whenever they can do so cheaply (without additional allocation), callers
are encouraged to use :const:`PY_VECTORCALL_ARGUMENTS_OFFSET`.
Doing so will allow callables such as bound methods to make their onward
@@ -430,6 +433,25 @@ Object Protocol
.. versionadded:: 3.8
+.. c:function:: PyObject* _PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+
+ Call a method using the vectorcall calling convention. The name of the method
+ is given as Python string *name*. The object whose method is called is
+ *args[0]* and the *args* array starting at *args[1]* represents the arguments
+ of the call. There must be at least one positional argument.
+ *nargsf* is the number of positional arguments including *args[0]*,
+ plus :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` if the value of ``args[0]`` may
+ temporarily be changed. Keyword arguments can be passed just like in
+ :c:func:`_PyObject_Vectorcall`.
+
+ If the object has the :const:`Py_TPFLAGS_METHOD_DESCRIPTOR` feature,
+ this will actually call the unbound method object with the full
+ *args* vector as arguments.
+
+ Return the result of the call on success, or raise an exception and return
+ *NULL* on failure.
+
+ .. versionadded:: 3.9
.. c:function:: Py_hash_t PyObject_Hash(PyObject *o)
diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index 415f3e1..b4288e7 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -158,6 +158,10 @@ PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
PyObject *args,
PyObject *kwargs);
+PyAPI_FUNC(PyObject *) _PyObject_VectorcallMethod(
+ PyObject *name, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames);
+
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
as the method name. */
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
@@ -174,6 +178,18 @@ PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(
struct _Py_Identifier *name,
...);
+static inline PyObject *
+_PyObject_VectorcallMethodId(
+ _Py_Identifier *name, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+ PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+ if (!oname) {
+ return NULL;
+ }
+ return _PyObject_VectorcallMethod(oname, args, nargsf, kwnames);
+}
+
PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o);
/* Guess the size of object 'o' using len(o) or o.__length_hint__().
diff --git a/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst
new file mode 100644
index 0000000..96ba2d0
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst
@@ -0,0 +1 @@
+Add :c:func:`_PyObject_VectorcallMethod` for fast calling of methods.
diff --git a/Modules/_abc.c b/Modules/_abc.c
index 1fbf3a8..7233690 100644
--- a/Modules/_abc.c
+++ b/Modules/_abc.c
@@ -480,6 +480,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/
{
PyObject *subtype, *result = NULL, *subclass = NULL;
+ PyObject *margs[2];
_abc_data *impl = _get_impl(self);
if (impl == NULL) {
return NULL;
@@ -514,12 +515,16 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
}
}
/* Fall back to the subclass check. */
- result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
- subclass, NULL);
+ margs[0] = self;
+ margs[1] = subclass;
+ result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
+ 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
goto end;
}
- result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
- subclass, NULL);
+ margs[0] = self;
+ margs[1] = subclass;
+ result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
+ 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
if (result == NULL) {
goto end;
}
@@ -531,8 +536,10 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
break;
case 0:
Py_DECREF(result);
- result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
- subtype, NULL);
+ margs[0] = self;
+ margs[1] = subtype;
+ result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
+ 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
break;
case 1: // Nothing to do.
break;
diff --git a/Objects/call.c b/Objects/call.c
index bde5513..2b52bdf 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -1078,6 +1078,38 @@ object_vacall(PyObject *base, PyObject *callable, va_list vargs)
PyObject *
+_PyObject_VectorcallMethod(PyObject *name, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+ assert(name != NULL);
+ assert(args != NULL);
+ assert(PyVectorcall_NARGS(nargsf) >= 1);
+
+ PyObject *callable = NULL;
+ /* Use args[0] as "self" argument */
+ int unbound = _PyObject_GetMethod(args[0], name, &callable);
+ if (callable == NULL) {
+ return NULL;
+ }
+
+ if (unbound) {
+ /* We must remove PY_VECTORCALL_ARGUMENTS_OFFSET since
+ * that would be interpreted as allowing to change args[-1] */
+ nargsf &= ~PY_VECTORCALL_ARGUMENTS_OFFSET;
+ }
+ else {
+ /* Skip "self". We can keep PY_VECTORCALL_ARGUMENTS_OFFSET since
+ * args[-1] in the onward call is args[0] here. */
+ args++;
+ nargsf--;
+ }
+ PyObject *result = _PyObject_Vectorcall(callable, args, nargsf, kwnames);
+ Py_DECREF(callable);
+ return result;
+}
+
+
+PyObject *
PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
{
if (obj == NULL || name == NULL) {
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 806c0af..a0eb505 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -851,15 +851,22 @@ static PySequenceMethods mappingproxy_as_sequence = {
};
static PyObject *
-mappingproxy_get(mappingproxyobject *pp, PyObject *args)
+mappingproxy_get(mappingproxyobject *pp, PyObject *const *args, Py_ssize_t nargs)
{
- PyObject *key, *def = Py_None;
- _Py_IDENTIFIER(get);
+ /* newargs: mapping, key, default=None */
+ PyObject *newargs[3];
+ newargs[0] = pp->mapping;
+ newargs[2] = Py_None;
- if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def))
+ if (!_PyArg_UnpackStack(args, nargs, "get", 1, 2,
+ &newargs[1], &newargs[2]))
+ {
return NULL;
- return _PyObject_CallMethodIdObjArgs(pp->mapping, &PyId_get,
- key, def, NULL);
+ }
+ _Py_IDENTIFIER(get);
+ return _PyObject_VectorcallMethodId(&PyId_get, newargs,
+ 3 | PY_VECTORCALL_ARGUMENTS_OFFSET,
+ NULL);
}
static PyObject *
@@ -894,7 +901,7 @@ mappingproxy_copy(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
to the underlying mapping */
static PyMethodDef mappingproxy_methods[] = {
- {"get", (PyCFunction)mappingproxy_get, METH_VARARGS,
+ {"get", (PyCFunction)mappingproxy_get, METH_FASTCALL,
PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
" d defaults to None.")},
{"keys", (PyCFunction)mappingproxy_keys, METH_NOARGS,
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index b200318..1c57561 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -3110,30 +3110,17 @@ PySys_SetArgv(int argc, wchar_t **argv)
static int
sys_pyfile_write_unicode(PyObject *unicode, PyObject *file)
{
- PyObject *writer = NULL, *result = NULL;
- int err;
-
if (file == NULL)
return -1;
-
- writer = _PyObject_GetAttrId(file, &PyId_write);
- if (writer == NULL)
- goto error;
-
- result = PyObject_CallFunctionObjArgs(writer, unicode, NULL);
+ assert(unicode != NULL);
+ PyObject *margs[2] = {file, unicode};
+ PyObject *result = _PyObject_VectorcallMethodId(&PyId_write, margs,
+ 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
if (result == NULL) {
- goto error;
- } else {
- err = 0;
- goto finally;
+ return -1;
}
-
-error:
- err = -1;
-finally:
- Py_XDECREF(writer);
- Py_XDECREF(result);
- return err;
+ Py_DECREF(result);
+ return 0;
}
static int