Skip to content

Commit 7e43373

Browse files
authored
bpo-38644: Add _PyObject_VectorcallTstate() (GH-17052)
* Add _PyObject_VectorcallTstate() function: similar to _PyObject_Vectorcall(), but with tstate parameter * Add tstate parameter to _PyObject_MakeTpCall()
1 parent befa032 commit 7e43373

File tree

6 files changed

+94
-50
lines changed

6 files changed

+94
-50
lines changed

Include/cpython/abstract.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(
4949
or _PyObject_FastCallDict() (both forms are supported),
5050
except that nargs is plainly the number of arguments without flags. */
5151
PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
52+
PyThreadState *tstate,
5253
PyObject *callable,
5354
PyObject *const *args, Py_ssize_t nargs,
5455
PyObject *keywords);
@@ -95,22 +96,31 @@ _PyVectorcall_Function(PyObject *callable)
9596
Return the result on success. Raise an exception and return NULL on
9697
error. */
9798
static inline PyObject *
98-
_PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
99-
size_t nargsf, PyObject *kwnames)
99+
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
100+
PyObject *const *args, size_t nargsf,
101+
PyObject *kwnames)
100102
{
101103
assert(kwnames == NULL || PyTuple_Check(kwnames));
102104
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
103105

104-
PyThreadState *tstate = PyThreadState_GET();
105106
vectorcallfunc func = _PyVectorcall_Function(callable);
106107
if (func == NULL) {
107108
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
108-
return _PyObject_MakeTpCall(callable, args, nargs, kwnames);
109+
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
109110
}
110111
PyObject *res = func(callable, args, nargsf, kwnames);
111112
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
112113
}
113114

115+
static inline PyObject *
116+
_PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
117+
size_t nargsf, PyObject *kwnames)
118+
{
119+
PyThreadState *tstate = PyThreadState_GET();
120+
return _PyObject_VectorcallTstate(tstate, callable,
121+
args, nargsf, kwnames);
122+
}
123+
114124
/* Same as _PyObject_Vectorcall except that keyword arguments are passed as
115125
dict, which may be NULL if there are no keyword arguments. */
116126
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(

Modules/_functoolsmodule.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,21 +132,25 @@ partial_dealloc(partialobject *pto)
132132
* if we would need to do that, we stop using vectorcall and fall back
133133
* to using partial_call() instead. */
134134
_Py_NO_INLINE static PyObject *
135-
partial_vectorcall_fallback(partialobject *pto, PyObject *const *args,
136-
size_t nargsf, PyObject *kwnames)
135+
partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto,
136+
PyObject *const *args, size_t nargsf,
137+
PyObject *kwnames)
137138
{
138139
pto->vectorcall = NULL;
139140
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
140-
return _PyObject_MakeTpCall((PyObject *)pto, args, nargs, kwnames);
141+
return _PyObject_MakeTpCall(tstate, (PyObject *)pto,
142+
args, nargs, kwnames);
141143
}
142144

143145
static PyObject *
144146
partial_vectorcall(partialobject *pto, PyObject *const *args,
145147
size_t nargsf, PyObject *kwnames)
146148
{
149+
PyThreadState *tstate = _PyThreadState_GET();
150+
147151
/* pto->kw is mutable, so need to check every time */
148152
if (PyDict_GET_SIZE(pto->kw)) {
149-
return partial_vectorcall_fallback(pto, args, nargsf, kwnames);
153+
return partial_vectorcall_fallback(tstate, pto, args, nargsf, kwnames);
150154
}
151155

152156
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
@@ -160,7 +164,8 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
160164

161165
/* Fast path if we're called without arguments */
162166
if (nargs_total == 0) {
163-
return _PyObject_Vectorcall(pto->fn, pto_args, pto_nargs, NULL);
167+
return _PyObject_VectorcallTstate(tstate, pto->fn,
168+
pto_args, pto_nargs, NULL);
164169
}
165170

166171
/* Fast path using PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single
@@ -169,7 +174,8 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
169174
PyObject **newargs = (PyObject **)args - 1;
170175
PyObject *tmp = newargs[0];
171176
newargs[0] = pto_args[0];
172-
PyObject *ret = _PyObject_Vectorcall(pto->fn, newargs, nargs + 1, kwnames);
177+
PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn,
178+
newargs, nargs + 1, kwnames);
173179
newargs[0] = tmp;
174180
return ret;
175181
}
@@ -195,7 +201,8 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
195201
memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*));
196202
memcpy(stack + pto_nargs, args, nargs_total * sizeof(PyObject*));
197203

198-
ret = _PyObject_Vectorcall(pto->fn, stack, pto_nargs + nargs, kwnames);
204+
ret = _PyObject_VectorcallTstate(tstate, pto->fn,
205+
stack, pto_nargs + nargs, kwnames);
199206
if (stack != small_stack) {
200207
PyMem_Free(stack);
201208
}

Objects/call.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
104104
vectorcallfunc func = _PyVectorcall_Function(callable);
105105
if (func == NULL) {
106106
/* Use tp_call instead */
107-
return _PyObject_MakeTpCall(callable, args, nargs, kwargs);
107+
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs);
108108
}
109109

110110
PyObject *res;
@@ -129,10 +129,10 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
129129

130130

131131
PyObject *
132-
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
132+
_PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,
133+
PyObject *const *args, Py_ssize_t nargs,
134+
PyObject *keywords)
133135
{
134-
PyThreadState *tstate = _PyThreadState_GET();
135-
136136
/* Slow path: build a temporary tuple for positional arguments and a
137137
* temporary dictionary for keyword arguments (if any) */
138138
ternaryfunc call = Py_TYPE(callable)->tp_call;
@@ -774,6 +774,7 @@ _PyObject_VectorcallMethod(PyObject *name, PyObject *const *args,
774774
assert(args != NULL);
775775
assert(PyVectorcall_NARGS(nargsf) >= 1);
776776

777+
PyThreadState *tstate = _PyThreadState_GET();
777778
PyObject *callable = NULL;
778779
/* Use args[0] as "self" argument */
779780
int unbound = _PyObject_GetMethod(args[0], name, &callable);
@@ -792,7 +793,8 @@ _PyObject_VectorcallMethod(PyObject *name, PyObject *const *args,
792793
args++;
793794
nargsf--;
794795
}
795-
PyObject *result = _PyObject_Vectorcall(callable, args, nargsf, kwnames);
796+
PyObject *result = _PyObject_VectorcallTstate(tstate, callable,
797+
args, nargsf, kwnames);
796798
Py_DECREF(callable);
797799
return result;
798800
}

Objects/classobject.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Python.h"
44
#include "pycore_object.h"
5+
#include "pycore_pyerrors.h"
56
#include "pycore_pymem.h"
67
#include "pycore_pystate.h"
78
#include "structmember.h"
@@ -37,25 +38,28 @@ method_vectorcall(PyObject *method, PyObject *const *args,
3738
size_t nargsf, PyObject *kwnames)
3839
{
3940
assert(Py_TYPE(method) == &PyMethod_Type);
40-
PyObject *self, *func, *result;
41-
self = PyMethod_GET_SELF(method);
42-
func = PyMethod_GET_FUNCTION(method);
41+
42+
PyThreadState *tstate = _PyThreadState_GET();
43+
PyObject *self = PyMethod_GET_SELF(method);
44+
PyObject *func = PyMethod_GET_FUNCTION(method);
4345
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
4446

47+
PyObject *result;
4548
if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) {
4649
/* PY_VECTORCALL_ARGUMENTS_OFFSET is set, so we are allowed to mutate the vector */
4750
PyObject **newargs = (PyObject**)args - 1;
4851
nargs += 1;
4952
PyObject *tmp = newargs[0];
5053
newargs[0] = self;
51-
result = _PyObject_Vectorcall(func, newargs, nargs, kwnames);
54+
result = _PyObject_VectorcallTstate(tstate, func, newargs,
55+
nargs, kwnames);
5256
newargs[0] = tmp;
5357
}
5458
else {
5559
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
5660
Py_ssize_t totalargs = nargs + nkwargs;
5761
if (totalargs == 0) {
58-
return _PyObject_Vectorcall(func, &self, 1, NULL);
62+
return _PyObject_VectorcallTstate(tstate, func, &self, 1, NULL);
5963
}
6064

6165
PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK];
@@ -66,7 +70,7 @@ method_vectorcall(PyObject *method, PyObject *const *args,
6670
else {
6771
newargs = PyMem_Malloc((totalargs+1) * sizeof(PyObject *));
6872
if (newargs == NULL) {
69-
PyErr_NoMemory();
73+
_PyErr_NoMemory(tstate);
7074
return NULL;
7175
}
7276
}
@@ -77,7 +81,8 @@ method_vectorcall(PyObject *method, PyObject *const *args,
7781
* undefined behaviour. */
7882
assert(args != NULL);
7983
memcpy(newargs + 1, args, totalargs * sizeof(PyObject *));
80-
result = _PyObject_Vectorcall(func, newargs, nargs+1, kwnames);
84+
result = _PyObject_VectorcallTstate(tstate, func,
85+
newargs, nargs+1, kwnames);
8186
if (newargs != newargs_stack) {
8287
PyMem_Free(newargs);
8388
}

Objects/typeobject.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,7 @@ lookup_method(PyObject *self, _Py_Identifier *attrid, int *unbound)
14451445

14461446

14471447
static inline PyObject*
1448-
vectorcall_unbound(int unbound, PyObject *func,
1448+
vectorcall_unbound(PyThreadState *tstate, int unbound, PyObject *func,
14491449
PyObject *const *args, Py_ssize_t nargs)
14501450
{
14511451
size_t nargsf = nargs;
@@ -1455,7 +1455,7 @@ vectorcall_unbound(int unbound, PyObject *func,
14551455
args++;
14561456
nargsf = nargsf - 1 + PY_VECTORCALL_ARGUMENTS_OFFSET;
14571457
}
1458-
return _PyObject_Vectorcall(func, args, nargsf, NULL);
1458+
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
14591459
}
14601460

14611461
static PyObject*
@@ -1479,24 +1479,27 @@ vectorcall_method(_Py_Identifier *name,
14791479
PyObject *const *args, Py_ssize_t nargs)
14801480
{
14811481
assert(nargs >= 1);
1482+
1483+
PyThreadState *tstate = _PyThreadState_GET();
14821484
int unbound;
14831485
PyObject *self = args[0];
14841486
PyObject *func = lookup_method(self, name, &unbound);
14851487
if (func == NULL) {
14861488
return NULL;
14871489
}
1488-
PyObject *retval = vectorcall_unbound(unbound, func, args, nargs);
1490+
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
14891491
Py_DECREF(func);
14901492
return retval;
14911493
}
14921494

14931495
/* Clone of vectorcall_method() that returns NotImplemented
14941496
* when the lookup fails. */
14951497
static PyObject *
1496-
vectorcall_maybe(_Py_Identifier *name,
1498+
vectorcall_maybe(PyThreadState *tstate, _Py_Identifier *name,
14971499
PyObject *const *args, Py_ssize_t nargs)
14981500
{
14991501
assert(nargs >= 1);
1502+
15001503
int unbound;
15011504
PyObject *self = args[0];
15021505
PyObject *func = lookup_maybe_method(self, name, &unbound);
@@ -1505,7 +1508,7 @@ vectorcall_maybe(_Py_Identifier *name,
15051508
Py_RETURN_NOTIMPLEMENTED;
15061509
return NULL;
15071510
}
1508-
PyObject *retval = vectorcall_unbound(unbound, func, args, nargs);
1511+
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
15091512
Py_DECREF(func);
15101513
return retval;
15111514
}
@@ -6177,6 +6180,7 @@ static PyObject * \
61776180
FUNCNAME(PyObject *self, PyObject *other) \
61786181
{ \
61796182
PyObject* stack[2]; \
6183+
PyThreadState *tstate = _PyThreadState_GET(); \
61806184
_Py_static_string(op_id, OPSTR); \
61816185
_Py_static_string(rop_id, ROPSTR); \
61826186
int do_other = Py_TYPE(self) != Py_TYPE(other) && \
@@ -6193,7 +6197,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
61936197
if (ok) { \
61946198
stack[0] = other; \
61956199
stack[1] = self; \
6196-
r = vectorcall_maybe(&rop_id, stack, 2); \
6200+
r = vectorcall_maybe(tstate, &rop_id, stack, 2); \
61976201
if (r != Py_NotImplemented) \
61986202
return r; \
61996203
Py_DECREF(r); \
@@ -6202,7 +6206,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
62026206
} \
62036207
stack[0] = self; \
62046208
stack[1] = other; \
6205-
r = vectorcall_maybe(&op_id, stack, 2); \
6209+
r = vectorcall_maybe(tstate, &op_id, stack, 2); \
62066210
if (r != Py_NotImplemented || \
62076211
Py_TYPE(other) == Py_TYPE(self)) \
62086212
return r; \
@@ -6211,7 +6215,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
62116215
if (do_other) { \
62126216
stack[0] = other; \
62136217
stack[1] = self; \
6214-
return vectorcall_maybe(&rop_id, stack, 2); \
6218+
return vectorcall_maybe(tstate, &rop_id, stack, 2); \
62156219
} \
62166220
Py_RETURN_NOTIMPLEMENTED; \
62176221
}
@@ -6293,6 +6297,7 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
62936297
static int
62946298
slot_sq_contains(PyObject *self, PyObject *value)
62956299
{
6300+
PyThreadState *tstate = _PyThreadState_GET();
62966301
PyObject *func, *res;
62976302
int result = -1, unbound;
62986303
_Py_IDENTIFIER(__contains__);
@@ -6307,7 +6312,7 @@ slot_sq_contains(PyObject *self, PyObject *value)
63076312
}
63086313
if (func != NULL) {
63096314
PyObject *args[2] = {self, value};
6310-
res = vectorcall_unbound(unbound, func, args, 2);
6315+
res = vectorcall_unbound(tstate, unbound, func, args, 2);
63116316
Py_DECREF(func);
63126317
if (res != NULL) {
63136318
result = PyObject_IsTrue(res);
@@ -6682,6 +6687,7 @@ static _Py_Identifier name_op[] = {
66826687
static PyObject *
66836688
slot_tp_richcompare(PyObject *self, PyObject *other, int op)
66846689
{
6690+
PyThreadState *tstate = _PyThreadState_GET();
66856691
int unbound;
66866692
PyObject *func, *res;
66876693

@@ -6692,7 +6698,7 @@ slot_tp_richcompare(PyObject *self, PyObject *other, int op)
66926698
}
66936699

66946700
PyObject *stack[2] = {self, other};
6695-
res = vectorcall_unbound(unbound, func, stack, 2);
6701+
res = vectorcall_unbound(tstate, unbound, func, stack, 2);
66966702
Py_DECREF(func);
66976703
return res;
66986704
}

0 commit comments

Comments
 (0)