Commit b1263d5a authored by Jeroen Demeyer's avatar Jeroen Demeyer Committed by Inada Naoki

bpo-37337: Add _PyObject_VectorcallMethod() (GH-14228)

parent b4bee030
...@@ -395,6 +395,9 @@ Object Protocol ...@@ -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 ...@@ -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( ...@@ -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( ...@@ -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__().
......
Add :c:func:`_PyObject_VectorcallMethod` for fast calling of methods.
...@@ -480,6 +480,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 = { ...@@ -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)) ...@@ -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) ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment