Skip to content

Commit c09fa75

Browse files
authored
GH-96237: Allow non-functions as reference-holder in frames. (GH-96238)
1 parent 8db7693 commit c09fa75

File tree

6 files changed

+26
-13
lines changed

6 files changed

+26
-13
lines changed

Include/internal/pycore_frame.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ enum _frameowner {
4747

4848
typedef struct _PyInterpreterFrame {
4949
/* "Specials" section */
50-
PyFunctionObject *f_func; /* Strong reference */
50+
PyObject *f_funcobj; /* Strong reference */
5151
PyObject *f_globals; /* Borrowed reference */
5252
PyObject *f_builtins; /* Borrowed reference */
5353
PyObject *f_locals; /* Strong reference, may be NULL */
@@ -101,7 +101,7 @@ _PyFrame_InitializeSpecials(
101101
_PyInterpreterFrame *frame, PyFunctionObject *func,
102102
PyObject *locals, PyCodeObject *code)
103103
{
104-
frame->f_func = func;
104+
frame->f_funcobj = (PyObject *)func;
105105
frame->f_code = (PyCodeObject *)Py_NewRef(code);
106106
frame->f_builtins = func->func_builtins;
107107
frame->f_globals = func->func_globals;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The internal field ``_PyInterpreterFrame.f_func`` is renamed to
2+
``_PyInterpreterFrame.f_funcobj`` and may be any object. The ``f_globals``
3+
and ``f_builtin`` fields may hold junk values.
4+
5+
It is safest to treat the ``_PyInterpreterFrame`` struct as opaque.

Modules/_testinternalcapi.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,9 @@ set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
511511
static PyObject *
512512
record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
513513
{
514-
PyList_Append(record_list, f->f_func->func_name);
514+
if (PyFunction_Check(f->f_funcobj)) {
515+
PyList_Append(record_list, ((PyFunctionObject *)f->f_funcobj)->func_name);
516+
}
515517
return _PyEval_EvalFrameDefault(tstate, f, exc);
516518
}
517519

Objects/frameobject.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ frame_dealloc(PyFrameObject *f)
910910
/* Don't clear code object until the end */
911911
co = frame->f_code;
912912
frame->f_code = NULL;
913-
Py_CLEAR(frame->f_func);
913+
Py_CLEAR(frame->f_funcobj);
914914
Py_CLEAR(frame->f_locals);
915915
PyObject **locals = _PyFrame_GetLocalsArray(frame);
916916
for (int i = 0; i < frame->stacktop; i++) {
@@ -1154,10 +1154,12 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
11541154
// COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt
11551155
// here:
11561156
int lasti = _PyInterpreterFrame_LASTI(frame);
1157-
if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) {
1157+
if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS
1158+
&& PyFunction_Check(frame->f_funcobj))
1159+
{
11581160
/* Free vars have not been initialized -- Do that */
11591161
PyCodeObject *co = frame->f_code;
1160-
PyObject *closure = frame->f_func->func_closure;
1162+
PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
11611163
int offset = co->co_nlocals + co->co_nplaincellvars;
11621164
for (int i = 0; i < co->co_nfreevars; ++i) {
11631165
PyObject *o = PyTuple_GET_ITEM(closure, i);

Python/ceval.c

+9-5
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,12 @@ lltrace_instruction(_PyInterpreterFrame *frame,
154154
static void
155155
lltrace_resume_frame(_PyInterpreterFrame *frame)
156156
{
157-
PyFunctionObject *f = frame->f_func;
158-
if (f == NULL) {
157+
PyObject *fobj = frame->f_funcobj;
158+
if (fobj == NULL || !PyFunction_Check(fobj)) {
159159
printf("\nResuming frame.");
160160
return;
161161
}
162+
PyFunctionObject *f = (PyFunctionObject *)fobj;
162163
PyObject *type, *value, *traceback;
163164
PyErr_Fetch(&type, &value, &traceback);
164165
PyObject *name = f->func_qualname;
@@ -2619,7 +2620,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
26192620
TARGET(COPY_FREE_VARS) {
26202621
/* Copy closure variables to free variables */
26212622
PyCodeObject *co = frame->f_code;
2622-
PyObject *closure = frame->f_func->func_closure;
2623+
assert(PyFunction_Check(frame->f_funcobj));
2624+
PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
26232625
int offset = co->co_nlocals + co->co_nplaincellvars;
26242626
assert(oparg == co->co_nfreevars);
26252627
for (int i = 0; i < oparg; ++i) {
@@ -4897,7 +4899,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
48974899
}
48984900

48994901
TARGET(RETURN_GENERATOR) {
4900-
PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(frame->f_func);
4902+
assert(PyFunction_Check(frame->f_funcobj));
4903+
PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj;
4904+
PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func);
49014905
if (gen == NULL) {
49024906
goto error;
49034907
}
@@ -4919,7 +4923,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
49194923
/* Make sure that frame is in a valid state */
49204924
frame->stacktop = 0;
49214925
frame->f_locals = NULL;
4922-
Py_INCREF(frame->f_func);
4926+
Py_INCREF(frame->f_funcobj);
49234927
Py_INCREF(frame->f_code);
49244928
/* Restore previous cframe and return. */
49254929
tstate->cframe = cframe.previous;

Python/frame.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg)
1313
{
1414
Py_VISIT(frame->frame_obj);
1515
Py_VISIT(frame->f_locals);
16-
Py_VISIT(frame->f_func);
16+
Py_VISIT(frame->f_funcobj);
1717
Py_VISIT(frame->f_code);
1818
/* locals */
1919
PyObject **locals = _PyFrame_GetLocalsArray(frame);
@@ -114,7 +114,7 @@ _PyFrame_Clear(_PyInterpreterFrame *frame)
114114
}
115115
Py_XDECREF(frame->frame_obj);
116116
Py_XDECREF(frame->f_locals);
117-
Py_DECREF(frame->f_func);
117+
Py_DECREF(frame->f_funcobj);
118118
Py_DECREF(frame->f_code);
119119
}
120120

0 commit comments

Comments
 (0)