bpo-38644: Pass tstate to _Py_CheckFunctionResult() (GH-17050)

* Add tstate parameter to _Py_CheckFunctionResult()
* Add _PyErr_FormatFromCauseTstate()
* Replace PyErr_XXX(...) with _PyErr_XXX(state, ...)
This commit is contained in:
Victor Stinner 2019-11-05 01:22:12 +01:00 committed by GitHub
parent be434dc038
commit 1726909094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 52 deletions

View File

@ -37,9 +37,11 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict(
40 bytes on the stack. */ 40 bytes on the stack. */
#define _PY_FASTCALL_SMALL_STACK 5 #define _PY_FASTCALL_SMALL_STACK 5
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable, PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(
PyObject *result, PyThreadState *tstate,
const char *where); PyObject *callable,
PyObject *result,
const char *where);
/* === Vectorcall protocol (PEP 590) ============================= */ /* === Vectorcall protocol (PEP 590) ============================= */
@ -98,13 +100,15 @@ _PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
{ {
assert(kwnames == NULL || PyTuple_Check(kwnames)); assert(kwnames == NULL || PyTuple_Check(kwnames));
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0); assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
PyThreadState *tstate = PyThreadState_GET();
vectorcallfunc func = _PyVectorcall_Function(callable); vectorcallfunc func = _PyVectorcall_Function(callable);
if (func == NULL) { if (func == NULL) {
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(callable, args, nargs, kwnames); return _PyObject_MakeTpCall(callable, args, nargs, kwnames);
} }
PyObject *res = func(callable, args, nargsf, kwnames); PyObject *res = func(callable, args, nargsf, kwnames);
return _Py_CheckFunctionResult(callable, res, NULL); return _Py_CheckFunctionResult(tstate, callable, res, NULL);
} }
/* Same as _PyObject_Vectorcall except that keyword arguments are passed as /* Same as _PyObject_Vectorcall except that keyword arguments are passed as

View File

@ -58,6 +58,12 @@ PyAPI_FUNC(void) _PyErr_NormalizeException(
PyObject **val, PyObject **val,
PyObject **tb); PyObject **tb);
PyAPI_FUNC(PyObject *) _PyErr_FormatFromCauseTstate(
PyThreadState *tstate,
PyObject *exception,
const char *format,
...);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -7,8 +7,9 @@
static PyObject *const * static PyObject *const *
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, _PyStack_UnpackDict(PyThreadState *tstate,
PyObject **p_kwnames); PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject **p_kwnames);
static void static void
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs, _PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
@ -26,22 +27,23 @@ null_error(void)
PyObject* PyObject*
_Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where) _Py_CheckFunctionResult(PyThreadState *tstate, PyObject *callable,
PyObject *result, const char *where)
{ {
int err_occurred = (PyErr_Occurred() != NULL); int err_occurred = (_PyErr_Occurred(tstate) != NULL);
assert((callable != NULL) ^ (where != NULL)); assert((callable != NULL) ^ (where != NULL));
if (result == NULL) { if (result == NULL) {
if (!err_occurred) { if (!err_occurred) {
if (callable) if (callable)
PyErr_Format(PyExc_SystemError, _PyErr_Format(tstate, PyExc_SystemError,
"%R returned NULL without setting an error", "%R returned NULL without setting an error",
callable); callable);
else else
PyErr_Format(PyExc_SystemError, _PyErr_Format(tstate, PyExc_SystemError,
"%s returned NULL without setting an error", "%s returned NULL without setting an error",
where); where);
#ifdef Py_DEBUG #ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode */ /* Ensure that the bug is caught in debug mode */
Py_FatalError("a function returned NULL without setting an error"); Py_FatalError("a function returned NULL without setting an error");
@ -54,14 +56,14 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where)
Py_DECREF(result); Py_DECREF(result);
if (callable) { if (callable) {
_PyErr_FormatFromCause(PyExc_SystemError, _PyErr_FormatFromCauseTstate(
"%R returned a result with an error set", tstate, PyExc_SystemError,
callable); "%R returned a result with an error set", callable);
} }
else { else {
_PyErr_FormatFromCause(PyExc_SystemError, _PyErr_FormatFromCauseTstate(
"%s returned a result with an error set", tstate, PyExc_SystemError,
where); "%s returned a result with an error set", where);
} }
#ifdef Py_DEBUG #ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode */ /* Ensure that the bug is caught in debug mode */
@ -88,11 +90,13 @@ PyObject *
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwargs) size_t nargsf, PyObject *kwargs)
{ {
assert(callable != NULL);
PyThreadState *tstate = _PyThreadState_GET();
/* _PyObject_FastCallDict() must not be called with an exception set, /* _PyObject_FastCallDict() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the because it can clear it (directly or indirectly) and so the
caller loses its exception */ caller loses its exception */
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
assert(callable != NULL);
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
assert(nargs >= 0); assert(nargs >= 0);
@ -112,7 +116,9 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
else { else {
PyObject *kwnames; PyObject *kwnames;
PyObject *const *newargs; PyObject *const *newargs;
newargs = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames); newargs = _PyStack_UnpackDict(tstate,
args, nargs,
kwargs, &kwnames);
if (newargs == NULL) { if (newargs == NULL) {
return NULL; return NULL;
} }
@ -120,7 +126,7 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
_PyStack_UnpackDict_Free(newargs, nargs, kwnames); _PyStack_UnpackDict_Free(newargs, nargs, kwnames);
} }
return _Py_CheckFunctionResult(callable, res, NULL); return _Py_CheckFunctionResult(tstate, callable, res, NULL);
} }
@ -177,7 +183,7 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
Py_DECREF(kwdict); Py_DECREF(kwdict);
} }
result = _Py_CheckFunctionResult(callable, result, NULL); result = _Py_CheckFunctionResult(tstate, callable, result, NULL);
return result; return result;
} }
@ -185,18 +191,22 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
PyObject * PyObject *
PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
{ {
PyThreadState *tstate = _PyThreadState_GET();
/* get vectorcallfunc as in _PyVectorcall_Function, but without /* get vectorcallfunc as in _PyVectorcall_Function, but without
* the _Py_TPFLAGS_HAVE_VECTORCALL check */ * the _Py_TPFLAGS_HAVE_VECTORCALL check */
Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset; Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset;
if (offset <= 0) { if (offset <= 0) {
PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall", _PyErr_Format(tstate, PyExc_TypeError,
Py_TYPE(callable)->tp_name); "'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
return NULL; return NULL;
} }
vectorcallfunc func = *(vectorcallfunc *)(((char *)callable) + offset); vectorcallfunc func = *(vectorcallfunc *)(((char *)callable) + offset);
if (func == NULL) { if (func == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall", _PyErr_Format(tstate, PyExc_TypeError,
Py_TYPE(callable)->tp_name); "'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
return NULL; return NULL;
} }
@ -210,14 +220,16 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
/* Convert arguments & call */ /* Convert arguments & call */
PyObject *const *args; PyObject *const *args;
PyObject *kwnames; PyObject *kwnames;
args = _PyStack_UnpackDict(_PyTuple_ITEMS(tuple), nargs, kwargs, &kwnames); args = _PyStack_UnpackDict(tstate,
_PyTuple_ITEMS(tuple), nargs,
kwargs, &kwnames);
if (args == NULL) { if (args == NULL) {
return NULL; return NULL;
} }
PyObject *result = func(callable, args, PyObject *result = func(callable, args,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
_PyStack_UnpackDict_Free(args, nargs, kwnames); _PyStack_UnpackDict_Free(args, nargs, kwnames);
return _Py_CheckFunctionResult(callable, result, NULL); return _Py_CheckFunctionResult(tstate, callable, result, NULL);
} }
@ -255,7 +267,7 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
_Py_LeaveRecursiveCall(tstate); _Py_LeaveRecursiveCall(tstate);
return _Py_CheckFunctionResult(callable, result, NULL); return _Py_CheckFunctionResult(tstate, callable, result, NULL);
} }
} }
@ -898,8 +910,9 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */ When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
static PyObject *const * static PyObject *const *
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, _PyStack_UnpackDict(PyThreadState *tstate,
PyObject **p_kwnames) PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject **p_kwnames)
{ {
assert(nargs >= 0); assert(nargs >= 0);
assert(kwargs != NULL); assert(kwargs != NULL);
@ -911,14 +924,14 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
* non-negative signed integers, so their difference fits in the type. */ * non-negative signed integers, so their difference fits in the type. */
Py_ssize_t maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1; Py_ssize_t maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1;
if (nargs > maxnargs - nkwargs) { if (nargs > maxnargs - nkwargs) {
PyErr_NoMemory(); _PyErr_NoMemory(tstate);
return NULL; return NULL;
} }
/* Add 1 to support PY_VECTORCALL_ARGUMENTS_OFFSET */ /* Add 1 to support PY_VECTORCALL_ARGUMENTS_OFFSET */
PyObject **stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0])); PyObject **stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0]));
if (stack == NULL) { if (stack == NULL) {
PyErr_NoMemory(); _PyErr_NoMemory(tstate);
return NULL; return NULL;
} }
@ -958,8 +971,8 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
* because it simplifies the deallocation in the failing case. * because it simplifies the deallocation in the failing case.
* It happens to also make the loop above slightly more efficient. */ * It happens to also make the loop above slightly more efficient. */
if (!keys_are_strings) { if (!keys_are_strings) {
PyErr_SetString(PyExc_TypeError, _PyErr_SetString(tstate, PyExc_TypeError,
"keywords must be strings"); "keywords must be strings");
_PyStack_UnpackDict_Free(stack, nargs, kwnames); _PyStack_UnpackDict_Free(stack, nargs, kwnames);
return NULL; return NULL;
} }

View File

@ -454,9 +454,11 @@ cfunction_vectorcall_O(
static PyObject * static PyObject *
cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
{ {
assert(!PyErr_Occurred());
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
PyThreadState *tstate = _PyThreadState_GET();
assert(!_PyErr_Occurred(tstate));
int flags = PyCFunction_GET_FLAGS(func); int flags = PyCFunction_GET_FLAGS(func);
if (!(flags & METH_VARARGS)) { if (!(flags & METH_VARARGS)) {
/* If this is not a METH_VARARGS function, delegate to vectorcall */ /* If this is not a METH_VARARGS function, delegate to vectorcall */
@ -474,11 +476,12 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
} }
else { else {
if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", _PyErr_Format(tstate, PyExc_TypeError,
((PyCFunctionObject*)func)->m_ml->ml_name); "%.200s() takes no keyword arguments",
((PyCFunctionObject*)func)->m_ml->ml_name);
return NULL; return NULL;
} }
result = meth(self, args); result = meth(self, args);
} }
return _Py_CheckFunctionResult(func, result, NULL); return _Py_CheckFunctionResult(tstate, func, result, NULL);
} }

View File

@ -2,6 +2,7 @@
#include "Python.h" #include "Python.h"
#include "pycore_object.h" #include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" #include "pycore_pystate.h"
#include "frameobject.h" #include "frameobject.h"
#include "structmember.h" #include "structmember.h"
@ -952,12 +953,12 @@ type_repr(PyTypeObject *type)
static PyObject * static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{ {
PyObject *obj; PyThreadState *tstate = _PyThreadState_GET();
if (type->tp_new == NULL) { if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, _PyErr_Format(tstate, PyExc_TypeError,
"cannot create '%.100s' instances", "cannot create '%.100s' instances",
type->tp_name); type->tp_name);
return NULL; return NULL;
} }
@ -965,11 +966,11 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
/* type_call() must not be called with an exception set, /* type_call() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the because it can clear it (directly or indirectly) and so the
caller loses its exception */ caller loses its exception */
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
#endif #endif
obj = type->tp_new(type, args, kwds); PyObject *obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL); obj = _Py_CheckFunctionResult(tstate, (PyObject*)type, obj, NULL);
if (obj == NULL) if (obj == NULL)
return NULL; return NULL;
@ -990,12 +991,12 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (type->tp_init != NULL) { if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds); int res = type->tp_init(obj, args, kwds);
if (res < 0) { if (res < 0) {
assert(PyErr_Occurred()); assert(_PyErr_Occurred(tstate));
Py_DECREF(obj); Py_DECREF(obj);
obj = NULL; obj = NULL;
} }
else { else {
assert(!PyErr_Occurred()); assert(!_PyErr_Occurred(tstate));
} }
} }
return obj; return obj;

View File

@ -3814,7 +3814,7 @@ exit_eval_frame:
f->f_executing = 0; f->f_executing = 0;
tstate->frame = f->f_back; tstate->frame = f->f_back;
return _Py_CheckFunctionResult(NULL, retval, "PyEval_EvalFrameEx"); return _Py_CheckFunctionResult(tstate, NULL, retval, "PyEval_EvalFrameEx");
} }
static void static void

View File

@ -520,6 +520,21 @@ _PyErr_FormatVFromCause(PyThreadState *tstate, PyObject *exception,
return NULL; return NULL;
} }
PyObject *
_PyErr_FormatFromCauseTstate(PyThreadState *tstate, PyObject *exception,
const char *format, ...)
{
va_list vargs;
#ifdef HAVE_STDARG_PROTOTYPES
va_start(vargs, format);
#else
va_start(vargs);
#endif
_PyErr_FormatVFromCause(tstate, exception, format, vargs);
va_end(vargs);
return NULL;
}
PyObject * PyObject *
_PyErr_FormatFromCause(PyObject *exception, const char *format, ...) _PyErr_FormatFromCause(PyObject *exception, const char *format, ...)
{ {