Skip to content

Commit 233ed46

Browse files
miss-islingtongaogaotiantianncoghlan
authored
[3.13] gh-118934: Make PyEval_GetLocals return borrowed reference (GH-119769) (#121869)
gh-118934: Make PyEval_GetLocals return borrowed reference (GH-119769) (cherry picked from commit e65cb4c) Co-authored-by: Tian Gao <[email protected]> Co-authored-by: Alyssa Coghlan <[email protected]>
1 parent 98e7d44 commit 233ed46

File tree

5 files changed

+42
-2
lines changed

5 files changed

+42
-2
lines changed

Include/internal/pycore_frame.h

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ struct _frame {
2626
char f_trace_lines; /* Emit per-line trace events? */
2727
char f_trace_opcodes; /* Emit per-opcode trace events? */
2828
PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */
29+
/* This is purely for backwards compatibility for PyEval_GetLocals.
30+
PyEval_GetLocals requires a borrowed reference so the actual reference
31+
is stored here */
32+
PyObject *f_locals_cache;
2933
/* The frame data, if this frame object owns the frame */
3034
PyObject *_f_frame_data[1];
3135
};

Lib/test/test_sys.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,7 @@ class C(object): pass
16031603
def func():
16041604
return sys._getframe()
16051605
x = func()
1606-
check(x, size('3Pi2cP7P2ic??2P'))
1606+
check(x, size('3Pi2c2P7P2ic??2P'))
16071607
# function
16081608
def func(): pass
16091609
check(func, size('15Pi'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make ``PyEval_GetLocals`` return borrowed reference

Objects/frameobject.c

+4
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,7 @@ frame_dealloc(PyFrameObject *f)
16261626
Py_CLEAR(f->f_back);
16271627
Py_CLEAR(f->f_trace);
16281628
Py_CLEAR(f->f_extra_locals);
1629+
Py_CLEAR(f->f_locals_cache);
16291630
PyObject_GC_Del(f);
16301631
Py_XDECREF(co);
16311632
Py_TRASHCAN_END;
@@ -1637,6 +1638,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
16371638
Py_VISIT(f->f_back);
16381639
Py_VISIT(f->f_trace);
16391640
Py_VISIT(f->f_extra_locals);
1641+
Py_VISIT(f->f_locals_cache);
16401642
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
16411643
return 0;
16421644
}
@@ -1649,6 +1651,7 @@ frame_tp_clear(PyFrameObject *f)
16491651
{
16501652
Py_CLEAR(f->f_trace);
16511653
Py_CLEAR(f->f_extra_locals);
1654+
Py_CLEAR(f->f_locals_cache);
16521655

16531656
/* locals and stack */
16541657
PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame);
@@ -1786,6 +1789,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
17861789
f->f_trace_opcodes = 0;
17871790
f->f_lineno = 0;
17881791
f->f_extra_locals = NULL;
1792+
f->f_locals_cache = NULL;
17891793
return f;
17901794
}
17911795

Python/ceval.c

+32-1
Original file line numberDiff line numberDiff line change
@@ -2466,14 +2466,45 @@ _PyEval_GetBuiltinId(_Py_Identifier *name)
24662466
PyObject *
24672467
PyEval_GetLocals(void)
24682468
{
2469+
// We need to return a borrowed reference here, so some tricks are needed
24692470
PyThreadState *tstate = _PyThreadState_GET();
24702471
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
24712472
if (current_frame == NULL) {
24722473
_PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist");
24732474
return NULL;
24742475
}
24752476

2476-
PyObject *locals = _PyEval_GetFrameLocals();
2477+
// Be aware that this returns a new reference
2478+
PyObject *locals = _PyFrame_GetLocals(current_frame);
2479+
2480+
if (locals == NULL) {
2481+
return NULL;
2482+
}
2483+
2484+
if (PyFrameLocalsProxy_Check(locals)) {
2485+
PyFrameObject *f = _PyFrame_GetFrameObject(current_frame);
2486+
PyObject *ret = f->f_locals_cache;
2487+
if (ret == NULL) {
2488+
PyObject *ret = PyDict_New();
2489+
if (ret == NULL) {
2490+
Py_DECREF(locals);
2491+
return NULL;
2492+
}
2493+
f->f_locals_cache = ret;
2494+
}
2495+
if (PyDict_Update(ret, locals) < 0) {
2496+
// At this point, if the cache dict is broken, it will stay broken, as
2497+
// trying to clean it up or replace it will just cause other problems
2498+
ret = NULL;
2499+
}
2500+
Py_DECREF(locals);
2501+
return ret;
2502+
}
2503+
2504+
assert(PyMapping_Check(locals));
2505+
assert(Py_REFCNT(locals) > 1);
2506+
Py_DECREF(locals);
2507+
24772508
return locals;
24782509
}
24792510

0 commit comments

Comments
 (0)