Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Include/cpython/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict(
40 bytes on the stack. */
#define _PY_FASTCALL_SMALL_STACK 5

PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
PyObject *result,
const char *where);
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(
PyThreadState *tstate,
PyObject *callable,
PyObject *result,
const char *where);

/* === Vectorcall protocol (PEP 590) ============================= */

Expand Down Expand Up @@ -98,13 +100,15 @@ _PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
{
assert(kwnames == NULL || PyTuple_Check(kwnames));
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);

PyThreadState *tstate = PyThreadState_GET();
vectorcallfunc func = _PyVectorcall_Function(callable);
if (func == NULL) {
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(callable, args, nargs, 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
Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ PyAPI_FUNC(void) _PyErr_NormalizeException(
PyObject **val,
PyObject **tb);

PyAPI_FUNC(PyObject *) _PyErr_FormatFromCauseTstate(
PyThreadState *tstate,
PyObject *exception,
const char *format,
...);

#ifdef __cplusplus
}
#endif
Expand Down
81 changes: 47 additions & 34 deletions Objects/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@


static PyObject *const *
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
PyObject **p_kwnames);
_PyStack_UnpackDict(PyThreadState *tstate,
PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject **p_kwnames);

static void
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
Expand All @@ -26,22 +27,23 @@ null_error(void)


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));

if (result == NULL) {
if (!err_occurred) {
if (callable)
PyErr_Format(PyExc_SystemError,
"%R returned NULL without setting an error",
callable);
_PyErr_Format(tstate, PyExc_SystemError,
"%R returned NULL without setting an error",
callable);
else
PyErr_Format(PyExc_SystemError,
"%s returned NULL without setting an error",
where);
_PyErr_Format(tstate, PyExc_SystemError,
"%s returned NULL without setting an error",
where);
#ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode */
Py_FatalError("a function returned NULL without setting an error");
Expand All @@ -54,14 +56,14 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where)
Py_DECREF(result);

if (callable) {
_PyErr_FormatFromCause(PyExc_SystemError,
"%R returned a result with an error set",
callable);
_PyErr_FormatFromCauseTstate(
tstate, PyExc_SystemError,
"%R returned a result with an error set", callable);
}
else {
_PyErr_FormatFromCause(PyExc_SystemError,
"%s returned a result with an error set",
where);
_PyErr_FormatFromCauseTstate(
tstate, PyExc_SystemError,
"%s returned a result with an error set", where);
}
#ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode */
Expand All @@ -88,11 +90,13 @@ PyObject *
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwargs)
{
assert(callable != NULL);

PyThreadState *tstate = _PyThreadState_GET();
/* _PyObject_FastCallDict() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(callable != NULL);
assert(!_PyErr_Occurred(tstate));

Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
assert(nargs >= 0);
Expand All @@ -112,15 +116,17 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
else {
PyObject *kwnames;
PyObject *const *newargs;
newargs = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames);
newargs = _PyStack_UnpackDict(tstate,
args, nargs,
kwargs, &kwnames);
if (newargs == NULL) {
return NULL;
}
res = func(callable, newargs,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
_PyStack_UnpackDict_Free(newargs, nargs, kwnames);
}
return _Py_CheckFunctionResult(callable, res, NULL);
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
}


Expand Down Expand Up @@ -177,26 +183,30 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
Py_DECREF(kwdict);
}

result = _Py_CheckFunctionResult(callable, result, NULL);
result = _Py_CheckFunctionResult(tstate, callable, result, NULL);
return result;
}


PyObject *
PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
{
PyThreadState *tstate = _PyThreadState_GET();

/* get vectorcallfunc as in _PyVectorcall_Function, but without
* the _Py_TPFLAGS_HAVE_VECTORCALL check */
Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset;
if (offset <= 0) {
PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
return NULL;
}
vectorcallfunc func = *(vectorcallfunc *)(((char *)callable) + offset);
if (func == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object does not support vectorcall",
Py_TYPE(callable)->tp_name);
return NULL;
}

Expand All @@ -210,14 +220,16 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
/* Convert arguments & call */
PyObject *const *args;
PyObject *kwnames;
args = _PyStack_UnpackDict(_PyTuple_ITEMS(tuple), nargs, kwargs, &kwnames);
args = _PyStack_UnpackDict(tstate,
_PyTuple_ITEMS(tuple), nargs,
kwargs, &kwnames);
if (args == NULL) {
return NULL;
}
PyObject *result = func(callable, args,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
_PyStack_UnpackDict_Free(args, nargs, kwnames);
return _Py_CheckFunctionResult(callable, result, NULL);
return _Py_CheckFunctionResult(tstate, callable, result, NULL);
}


Expand Down Expand Up @@ -255,7 +267,7 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)

_Py_LeaveRecursiveCall(tstate);

return _Py_CheckFunctionResult(callable, result, NULL);
return _Py_CheckFunctionResult(tstate, callable, result, NULL);
}
}

Expand Down Expand Up @@ -898,8 +910,9 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)

When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
static PyObject *const *
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
PyObject **p_kwnames)
_PyStack_UnpackDict(PyThreadState *tstate,
PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject **p_kwnames)
{
assert(nargs >= 0);
assert(kwargs != NULL);
Expand All @@ -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. */
Py_ssize_t maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1;
if (nargs > maxnargs - nkwargs) {
PyErr_NoMemory();
_PyErr_NoMemory(tstate);
return NULL;
}

/* Add 1 to support PY_VECTORCALL_ARGUMENTS_OFFSET */
PyObject **stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0]));
if (stack == NULL) {
PyErr_NoMemory();
_PyErr_NoMemory(tstate);
return NULL;
}

Expand Down Expand Up @@ -958,8 +971,8 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
* because it simplifies the deallocation in the failing case.
* It happens to also make the loop above slightly more efficient. */
if (!keys_are_strings) {
PyErr_SetString(PyExc_TypeError,
"keywords must be strings");
_PyErr_SetString(tstate, PyExc_TypeError,
"keywords must be strings");
_PyStack_UnpackDict_Free(stack, nargs, kwnames);
return NULL;
}
Expand Down
11 changes: 7 additions & 4 deletions Objects/methodobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,11 @@ cfunction_vectorcall_O(
static PyObject *
cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
{
assert(!PyErr_Occurred());
assert(kwargs == NULL || PyDict_Check(kwargs));

PyThreadState *tstate = _PyThreadState_GET();
assert(!_PyErr_Occurred(tstate));

int flags = PyCFunction_GET_FLAGS(func);
if (!(flags & METH_VARARGS)) {
/* If this is not a METH_VARARGS function, delegate to vectorcall */
Expand All @@ -474,11 +476,12 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
}
else {
if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
((PyCFunctionObject*)func)->m_ml->ml_name);
_PyErr_Format(tstate, PyExc_TypeError,
"%.200s() takes no keyword arguments",
((PyCFunctionObject*)func)->m_ml->ml_name);
return NULL;
}
result = meth(self, args);
}
return _Py_CheckFunctionResult(func, result, NULL);
return _Py_CheckFunctionResult(tstate, func, result, NULL);
}
19 changes: 10 additions & 9 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Python.h"
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#include "frameobject.h"
#include "structmember.h"
Expand Down Expand Up @@ -952,24 +953,24 @@ type_repr(PyTypeObject *type)
static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *obj;
PyThreadState *tstate = _PyThreadState_GET();

if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError,
"cannot create '%.100s' instances",
type->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"cannot create '%.100s' instances",
type->tp_name);
return NULL;
}

#ifdef Py_DEBUG
/* type_call() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(!_PyErr_Occurred(tstate));
#endif

obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL);
PyObject *obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult(tstate, (PyObject*)type, obj, NULL);
if (obj == NULL)
return NULL;

Expand All @@ -990,12 +991,12 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds);
if (res < 0) {
assert(PyErr_Occurred());
assert(_PyErr_Occurred(tstate));
Py_DECREF(obj);
obj = NULL;
}
else {
assert(!PyErr_Occurred());
assert(!_PyErr_Occurred(tstate));
}
}
return obj;
Expand Down
2 changes: 1 addition & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -3814,7 +3814,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
f->f_executing = 0;
tstate->frame = f->f_back;

return _Py_CheckFunctionResult(NULL, retval, "PyEval_EvalFrameEx");
return _Py_CheckFunctionResult(tstate, NULL, retval, "PyEval_EvalFrameEx");
}

static void
Expand Down
15 changes: 15 additions & 0 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,21 @@ _PyErr_FormatVFromCause(PyThreadState *tstate, PyObject *exception,
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 *
_PyErr_FormatFromCause(PyObject *exception, const char *format, ...)
{
Expand Down