mirror of https://github.com/python/cpython
gh-118934: Make PyEval_GetLocals return borrowed reference (#119769)
Co-authored-by: Alyssa Coghlan <ncoghlan@gmail.com>
This commit is contained in:
parent
162b41f577
commit
e65cb4c6f0
|
@ -27,6 +27,10 @@ struct _frame {
|
||||||
char f_trace_lines; /* Emit per-line trace events? */
|
char f_trace_lines; /* Emit per-line trace events? */
|
||||||
char f_trace_opcodes; /* Emit per-opcode trace events? */
|
char f_trace_opcodes; /* Emit per-opcode trace events? */
|
||||||
PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */
|
PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */
|
||||||
|
/* This is purely for backwards compatibility for PyEval_GetLocals.
|
||||||
|
PyEval_GetLocals requires a borrowed reference so the actual reference
|
||||||
|
is stored here */
|
||||||
|
PyObject *f_locals_cache;
|
||||||
/* The frame data, if this frame object owns the frame */
|
/* The frame data, if this frame object owns the frame */
|
||||||
PyObject *_f_frame_data[1];
|
PyObject *_f_frame_data[1];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1603,7 +1603,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
def func():
|
def func():
|
||||||
return sys._getframe()
|
return sys._getframe()
|
||||||
x = func()
|
x = func()
|
||||||
check(x, size('3Pi2cP7P2ic??2P'))
|
check(x, size('3Pi2c2P7P2ic??2P'))
|
||||||
# function
|
# function
|
||||||
def func(): pass
|
def func(): pass
|
||||||
check(func, size('16Pi'))
|
check(func, size('16Pi'))
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Make ``PyEval_GetLocals`` return borrowed reference
|
|
@ -1627,6 +1627,7 @@ frame_dealloc(PyFrameObject *f)
|
||||||
Py_CLEAR(f->f_back);
|
Py_CLEAR(f->f_back);
|
||||||
Py_CLEAR(f->f_trace);
|
Py_CLEAR(f->f_trace);
|
||||||
Py_CLEAR(f->f_extra_locals);
|
Py_CLEAR(f->f_extra_locals);
|
||||||
|
Py_CLEAR(f->f_locals_cache);
|
||||||
PyObject_GC_Del(f);
|
PyObject_GC_Del(f);
|
||||||
Py_XDECREF(co);
|
Py_XDECREF(co);
|
||||||
Py_TRASHCAN_END;
|
Py_TRASHCAN_END;
|
||||||
|
@ -1638,6 +1639,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
|
||||||
Py_VISIT(f->f_back);
|
Py_VISIT(f->f_back);
|
||||||
Py_VISIT(f->f_trace);
|
Py_VISIT(f->f_trace);
|
||||||
Py_VISIT(f->f_extra_locals);
|
Py_VISIT(f->f_extra_locals);
|
||||||
|
Py_VISIT(f->f_locals_cache);
|
||||||
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
|
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1650,6 +1652,7 @@ frame_tp_clear(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
Py_CLEAR(f->f_trace);
|
Py_CLEAR(f->f_trace);
|
||||||
Py_CLEAR(f->f_extra_locals);
|
Py_CLEAR(f->f_extra_locals);
|
||||||
|
Py_CLEAR(f->f_locals_cache);
|
||||||
|
|
||||||
/* locals and stack */
|
/* locals and stack */
|
||||||
_PyStackRef *locals = _PyFrame_GetLocalsArray(f->f_frame);
|
_PyStackRef *locals = _PyFrame_GetLocalsArray(f->f_frame);
|
||||||
|
@ -1787,6 +1790,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
|
||||||
f->f_trace_opcodes = 0;
|
f->f_trace_opcodes = 0;
|
||||||
f->f_lineno = 0;
|
f->f_lineno = 0;
|
||||||
f->f_extra_locals = NULL;
|
f->f_extra_locals = NULL;
|
||||||
|
f->f_locals_cache = NULL;
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2525,6 +2525,7 @@ _PyEval_GetBuiltinId(_Py_Identifier *name)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyEval_GetLocals(void)
|
PyEval_GetLocals(void)
|
||||||
{
|
{
|
||||||
|
// We need to return a borrowed reference here, so some tricks are needed
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
|
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
|
||||||
if (current_frame == NULL) {
|
if (current_frame == NULL) {
|
||||||
|
@ -2532,7 +2533,37 @@ PyEval_GetLocals(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *locals = _PyEval_GetFrameLocals();
|
// Be aware that this returns a new reference
|
||||||
|
PyObject *locals = _PyFrame_GetLocals(current_frame);
|
||||||
|
|
||||||
|
if (locals == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyFrameLocalsProxy_Check(locals)) {
|
||||||
|
PyFrameObject *f = _PyFrame_GetFrameObject(current_frame);
|
||||||
|
PyObject *ret = f->f_locals_cache;
|
||||||
|
if (ret == NULL) {
|
||||||
|
PyObject *ret = PyDict_New();
|
||||||
|
if (ret == NULL) {
|
||||||
|
Py_DECREF(locals);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
f->f_locals_cache = ret;
|
||||||
|
}
|
||||||
|
if (PyDict_Update(ret, locals) < 0) {
|
||||||
|
// At this point, if the cache dict is broken, it will stay broken, as
|
||||||
|
// trying to clean it up or replace it will just cause other problems
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(locals);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(PyMapping_Check(locals));
|
||||||
|
assert(Py_REFCNT(locals) > 1);
|
||||||
|
Py_DECREF(locals);
|
||||||
|
|
||||||
return locals;
|
return locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue