Skip to content
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

gh-118934: Make PyEval_GetLocals return borrowed reference #119769

Merged
merged 10 commits into from
Jul 16, 2024
4 changes: 4 additions & 0 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ struct _frame {
char f_trace_lines; /* Emit per-line 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 */
/* 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 */
PyObject *_f_frame_data[1];
};
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ class C(object): pass
def func():
return sys._getframe()
x = func()
check(x, size('3Pi2cP7P2ic??2P'))
check(x, size('3Pi2c2P7P2ic??2P'))
# function
def func(): pass
check(func, size('16Pi'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make ``PyEval_GetLocals`` return borrowed reference
4 changes: 4 additions & 0 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1626,6 +1626,7 @@ frame_dealloc(PyFrameObject *f)
Py_CLEAR(f->f_back);
Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
Py_CLEAR(f->f_locals_cache);
PyObject_GC_Del(f);
Py_XDECREF(co);
Py_TRASHCAN_END;
Expand All @@ -1637,6 +1638,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
Py_VISIT(f->f_back);
Py_VISIT(f->f_trace);
Py_VISIT(f->f_extra_locals);
Py_VISIT(f->f_locals_cache);
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
return 0;
}
Expand All @@ -1649,6 +1651,7 @@ frame_tp_clear(PyFrameObject *f)
{
Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
Py_CLEAR(f->f_locals_cache);

/* locals and stack */
PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame);
Expand Down Expand Up @@ -1786,6 +1789,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
f->f_trace_opcodes = 0;
f->f_lineno = 0;
f->f_extra_locals = NULL;
f->f_locals_cache = NULL;
return f;
}

Expand Down
30 changes: 29 additions & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2476,14 +2476,42 @@ _PyEval_GetBuiltinId(_Py_Identifier *name)
PyObject *
PyEval_GetLocals(void)
{
// We need to return a borrowed reference here, so some tricks are needed
PyThreadState *tstate = _PyThreadState_GET();
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
if (current_frame == NULL) {
_PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist");
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 = PyDict_New();
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved
if (ret == NULL) {
Py_DECREF(locals);
return NULL;
}
if (PyDict_Update(ret, locals) < 0) {
Py_DECREF(ret);
Py_DECREF(locals);
return NULL;
}
Py_DECREF(locals);
Py_XSETREF(f->f_locals_cache, ret);
return ret;
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved
}

assert(PyMapping_Check(locals));
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved
assert(Py_REFCNT(locals) > 1);
Py_DECREF(locals);

return locals;
}

Expand Down
Loading