Skip to content

bpo-38644: Pass tstate to Py_EnterRecursiveCall() #16997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2019
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
45 changes: 31 additions & 14 deletions Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,31 @@ PyAPI_DATA(int) _Py_CheckRecursionLimit;
#ifdef USE_STACKCHECK
/* With USE_STACKCHECK macro defined, trigger stack checks in
_Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */
# define _Py_MakeRecCheck(x) \
(++(x) > _Py_CheckRecursionLimit || \
++(PyThreadState_GET()->stackcheck_counter) > 64)
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
return (++tstate->recursion_depth > _Py_CheckRecursionLimit
|| ++tstate->stackcheck_counter > 64);
}
#else
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
return (++tstate->recursion_depth > _Py_CheckRecursionLimit);
}
#endif

PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
PyAPI_FUNC(int) _Py_CheckRecursiveCall(
PyThreadState *tstate,
const char *where);

static inline int _Py_EnterRecursiveCall(PyThreadState *tstate,
const char *where) {
return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
}

#define _Py_EnterRecursiveCall_macro(where) \
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
_Py_CheckRecursiveCall(where))
static inline int _Py_EnterRecursiveCall_inline(const char *where) {
PyThreadState *tstate = PyThreadState_GET();
return _Py_EnterRecursiveCall(tstate, where);
}

#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_macro(where)
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)


/* Compute the "lower-water mark" for a recursion limit. When
Expand All @@ -38,12 +49,18 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
#define _Py_MakeEndRecCheck(x) \
(--(x) < _Py_RecursionLimitLowerWaterMark(_Py_CheckRecursionLimit))

#define _Py_LeaveRecursiveCall_macro() \
do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \
PyThreadState_GET()->overflowed = 0; \
} while(0)
static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
if (_Py_MakeEndRecCheck(tstate->recursion_depth)) {
tstate->overflowed = 0;
}
}

static inline void _Py_LeaveRecursiveCall_inline(void) {
PyThreadState *tstate = PyThreadState_GET();
_Py_LeaveRecursiveCall(tstate);
}

#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_macro()
#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline()

#ifdef __cplusplus
}
Expand Down
80 changes: 49 additions & 31 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Abstract Object Interface (many thanks to Jim Fulton) */

#include "Python.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#include <ctype.h>
#include "structmember.h" /* we need the offsetof() macro from there */
Expand Down Expand Up @@ -2459,8 +2460,8 @@ recursive_isinstance(PyObject *inst, PyObject *cls)
return retval;
}

int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
static int
object_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls)
{
_Py_IDENTIFIER(__instancecheck__);
PyObject *checker;
Expand All @@ -2475,47 +2476,55 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
}

if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;

if (Py_EnterRecursiveCall(" in __instancecheck__"))
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
int r = 0;
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsInstance(inst, item);
r = object_isinstance(tstate, inst, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);
return r;
}

checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
PyObject *res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __instancecheck__")) {
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
Py_DECREF(checker);
return ok;
}
res = _PyObject_CallOneArg(checker, inst);
Py_LeaveRecursiveCall();
PyObject *res = _PyObject_CallOneArg(checker, inst);
_Py_LeaveRecursiveCall(tstate);
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
}
else if (PyErr_Occurred())
else if (_PyErr_Occurred(tstate)) {
return -1;
}

/* Probably never reached anymore. */
return recursive_isinstance(inst, cls);
}


int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
PyThreadState *tstate = _PyThreadState_GET();
return object_isinstance(tstate, inst, cls);
}


static int
recursive_issubclass(PyObject *derived, PyObject *cls)
{
Expand All @@ -2534,8 +2543,8 @@ recursive_issubclass(PyObject *derived, PyObject *cls)
return abstract_issubclass(derived, cls);
}

int
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
static int
object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
{
_Py_IDENTIFIER(__subclasscheck__);
PyObject *checker;
Expand All @@ -2549,47 +2558,56 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
}

if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;

if (Py_EnterRecursiveCall(" in __subclasscheck__"))
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
int r = 0;
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsSubclass(derived, item);
r = object_issubclass(tstate, derived, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);
return r;
}

checker = _PyObject_LookupSpecial(cls, &PyId___subclasscheck__);
if (checker != NULL) {
PyObject *res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
Py_DECREF(checker);
return ok;
}
res = _PyObject_CallOneArg(checker, derived);
Py_LeaveRecursiveCall();
PyObject *res = _PyObject_CallOneArg(checker, derived);
_Py_LeaveRecursiveCall(tstate);
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
}
else if (PyErr_Occurred())
else if (_PyErr_Occurred(tstate)) {
return -1;
}

/* Probably never reached anymore. */
return recursive_issubclass(derived, cls);
}


int
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
{
PyThreadState *tstate = _PyThreadState_GET();
return object_issubclass(tstate, derived, cls);
}


int
_PyObject_RealIsInstance(PyObject *inst, PyObject *cls)
{
Expand Down
42 changes: 23 additions & 19 deletions Objects/call.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Python.h"
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#include "pycore_tupleobject.h"
#include "frameobject.h"
Expand Down Expand Up @@ -126,12 +127,15 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
PyObject *
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
{
PyThreadState *tstate = _PyThreadState_GET();

/* Slow path: build a temporary tuple for positional arguments and a
* temporary dictionary for keyword arguments (if any) */
ternaryfunc call = Py_TYPE(callable)->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
Py_TYPE(callable)->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not callable",
Py_TYPE(callable)->tp_name);
return NULL;
}

Expand Down Expand Up @@ -162,10 +166,10 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
}

PyObject *result = NULL;
if (Py_EnterRecursiveCall(" while calling a Python object") == 0)
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object") == 0)
{
result = call(callable, argstuple, kwdict);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);
}

Py_DECREF(argstuple);
Expand Down Expand Up @@ -220,13 +224,14 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
PyObject *
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
{
PyThreadState *tstate = _PyThreadState_GET();
ternaryfunc call;
PyObject *result;

/* PyObject_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));
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));

Expand All @@ -236,17 +241,19 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
else {
call = callable->ob_type->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not callable",
callable->ob_type->tp_name);
return NULL;
}

if (Py_EnterRecursiveCall(" while calling a Python object"))
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
return NULL;
}

result = (*call)(callable, args, kwargs);

Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);

return _Py_CheckFunctionResult(callable, result, NULL);
}
Expand All @@ -266,30 +273,27 @@ static PyObject* _Py_HOT_FUNCTION
function_code_fastcall(PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs,
PyObject *globals)
{
PyFrameObject *f;
assert(globals != NULL);

PyThreadState *tstate = _PyThreadState_GET();
PyObject **fastlocals;
Py_ssize_t i;
PyObject *result;
assert(tstate != NULL);

assert(globals != NULL);
/* XXX Perhaps we should create a specialized
_PyFrame_New_NoTrack() that doesn't take locals, but does
take builtins without sanity checking them.
*/
assert(tstate != NULL);
f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
if (f == NULL) {
return NULL;
}

fastlocals = f->f_localsplus;
PyObject **fastlocals = f->f_localsplus;

for (i = 0; i < nargs; i++) {
for (Py_ssize_t i = 0; i < nargs; i++) {
Py_INCREF(*args);
fastlocals[i] = *args++;
}
result = PyEval_EvalFrameEx(f,0);
PyObject *result = PyEval_EvalFrameEx(f, 0);

if (Py_REFCNT(f) > 1) {
Py_DECREF(f);
Expand Down
Loading