mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-37337: Add _PyObject_VectorcallMethod() (GH-14228)
This commit is contained in:
parent
b4bee03087
commit
b1263d5a60
7 changed files with 105 additions and 33 deletions
|
@ -395,6 +395,9 @@ Object Protocol
|
||||||
argument 1 (not 0) in the allocated vector.
|
argument 1 (not 0) in the allocated vector.
|
||||||
The callee must restore the value of ``args[-1]`` before returning.
|
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
|
Whenever they can do so cheaply (without additional allocation), callers
|
||||||
are encouraged to use :const:`PY_VECTORCALL_ARGUMENTS_OFFSET`.
|
are encouraged to use :const:`PY_VECTORCALL_ARGUMENTS_OFFSET`.
|
||||||
Doing so will allow callables such as bound methods to make their onward
|
Doing so will allow callables such as bound methods to make their onward
|
||||||
|
@ -430,6 +433,25 @@ Object Protocol
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. 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)
|
.. c:function:: Py_hash_t PyObject_Hash(PyObject *o)
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,10 @@ PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
|
||||||
PyObject *args,
|
PyObject *args,
|
||||||
PyObject *kwargs);
|
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*
|
/* 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,
|
||||||
|
@ -174,6 +178,18 @@ PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(
|
||||||
struct _Py_Identifier *name,
|
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);
|
PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o);
|
||||||
|
|
||||||
/* Guess the size of object 'o' using len(o) or o.__length_hint__().
|
/* Guess the size of object 'o' using len(o) or o.__length_hint__().
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add :c:func:`_PyObject_VectorcallMethod` for fast calling of methods.
|
|
@ -480,6 +480,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
|
||||||
/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/
|
/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/
|
||||||
{
|
{
|
||||||
PyObject *subtype, *result = NULL, *subclass = NULL;
|
PyObject *subtype, *result = NULL, *subclass = NULL;
|
||||||
|
PyObject *margs[2];
|
||||||
_abc_data *impl = _get_impl(self);
|
_abc_data *impl = _get_impl(self);
|
||||||
if (impl == NULL) {
|
if (impl == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -514,12 +515,16 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Fall back to the subclass check. */
|
/* Fall back to the subclass check. */
|
||||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
margs[0] = self;
|
||||||
subclass, NULL);
|
margs[1] = subclass;
|
||||||
|
result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
|
||||||
|
2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
margs[0] = self;
|
||||||
subclass, NULL);
|
margs[1] = subclass;
|
||||||
|
result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
|
||||||
|
2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -531,8 +536,10 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
margs[0] = self;
|
||||||
subtype, NULL);
|
margs[1] = subtype;
|
||||||
|
result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs,
|
||||||
|
2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||||
break;
|
break;
|
||||||
case 1: // Nothing to do.
|
case 1: // Nothing to do.
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1077,6 +1077,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 *
|
||||||
PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
|
PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
|
||||||
{
|
{
|
||||||
|
|
|
@ -851,15 +851,22 @@ static PySequenceMethods mappingproxy_as_sequence = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mappingproxy_get(mappingproxyobject *pp, PyObject *args)
|
mappingproxy_get(mappingproxyobject *pp, PyObject *const *args, Py_ssize_t nargs)
|
||||||
{
|
{
|
||||||
PyObject *key, *def = Py_None;
|
/* newargs: mapping, key, default=None */
|
||||||
_Py_IDENTIFIER(get);
|
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 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 *
|
static PyObject *
|
||||||
|
@ -894,7 +901,7 @@ mappingproxy_copy(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
|
||||||
to the underlying mapping */
|
to the underlying mapping */
|
||||||
|
|
||||||
static PyMethodDef mappingproxy_methods[] = {
|
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."
|
PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
|
||||||
" d defaults to None.")},
|
" d defaults to None.")},
|
||||||
{"keys", (PyCFunction)mappingproxy_keys, METH_NOARGS,
|
{"keys", (PyCFunction)mappingproxy_keys, METH_NOARGS,
|
||||||
|
|
|
@ -3110,30 +3110,17 @@ PySys_SetArgv(int argc, wchar_t **argv)
|
||||||
static int
|
static int
|
||||||
sys_pyfile_write_unicode(PyObject *unicode, PyObject *file)
|
sys_pyfile_write_unicode(PyObject *unicode, PyObject *file)
|
||||||
{
|
{
|
||||||
PyObject *writer = NULL, *result = NULL;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
assert(unicode != NULL);
|
||||||
writer = _PyObject_GetAttrId(file, &PyId_write);
|
PyObject *margs[2] = {file, unicode};
|
||||||
if (writer == NULL)
|
PyObject *result = _PyObject_VectorcallMethodId(&PyId_write, margs,
|
||||||
goto error;
|
2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||||
|
|
||||||
result = PyObject_CallFunctionObjArgs(writer, unicode, NULL);
|
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
goto error;
|
return -1;
|
||||||
} else {
|
|
||||||
err = 0;
|
|
||||||
goto finally;
|
|
||||||
}
|
}
|
||||||
|
Py_DECREF(result);
|
||||||
error:
|
return 0;
|
||||||
err = -1;
|
|
||||||
finally:
|
|
||||||
Py_XDECREF(writer);
|
|
||||||
Py_XDECREF(result);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue