From c09fa7542c6d9b724e423b14c6fb5f4338eabd12 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 25 Aug 2022 10:16:55 +0100 Subject: GH-96237: Allow non-functions as reference-holder in frames. (GH-96238) --- Include/internal/pycore_frame.h | 4 ++-- .../2022-08-24-14-30-26.gh-issue-96237.msif5f.rst | 5 +++++ Modules/_testinternalcapi.c | 4 +++- Objects/frameobject.c | 8 +++++--- Python/ceval.c | 14 +++++++++----- Python/frame.c | 4 ++-- 6 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-08-24-14-30-26.gh-issue-96237.msif5f.rst diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 994c205..decaafd 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -47,7 +47,7 @@ enum _frameowner { typedef struct _PyInterpreterFrame { /* "Specials" section */ - PyFunctionObject *f_func; /* Strong reference */ + PyObject *f_funcobj; /* Strong reference */ PyObject *f_globals; /* Borrowed reference */ PyObject *f_builtins; /* Borrowed reference */ PyObject *f_locals; /* Strong reference, may be NULL */ @@ -101,7 +101,7 @@ _PyFrame_InitializeSpecials( _PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals, PyCodeObject *code) { - frame->f_func = func; + frame->f_funcobj = (PyObject *)func; frame->f_code = (PyCodeObject *)Py_NewRef(code); frame->f_builtins = func->func_builtins; frame->f_globals = func->func_globals; diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-08-24-14-30-26.gh-issue-96237.msif5f.rst b/Misc/NEWS.d/next/Core and Builtins/2022-08-24-14-30-26.gh-issue-96237.msif5f.rst new file mode 100644 index 0000000..cb8a1c0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-08-24-14-30-26.gh-issue-96237.msif5f.rst @@ -0,0 +1,5 @@ +The internal field ``_PyInterpreterFrame.f_func`` is renamed to +``_PyInterpreterFrame.f_funcobj`` and may be any object. The ``f_globals`` +and ``f_builtin`` fields may hold junk values. + +It is safest to treat the ``_PyInterpreterFrame`` struct as opaque. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 9d92b07..02a061b 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -511,7 +511,9 @@ set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc) { - PyList_Append(record_list, f->f_func->func_name); + if (PyFunction_Check(f->f_funcobj)) { + PyList_Append(record_list, ((PyFunctionObject *)f->f_funcobj)->func_name); + } return _PyEval_EvalFrameDefault(tstate, f, exc); } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 26b38ba..d2647bd 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -910,7 +910,7 @@ frame_dealloc(PyFrameObject *f) /* Don't clear code object until the end */ co = frame->f_code; frame->f_code = NULL; - Py_CLEAR(frame->f_func); + Py_CLEAR(frame->f_funcobj); Py_CLEAR(frame->f_locals); PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < frame->stacktop; i++) { @@ -1154,10 +1154,12 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { // COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt // here: int lasti = _PyInterpreterFrame_LASTI(frame); - if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) { + if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS + && PyFunction_Check(frame->f_funcobj)) + { /* Free vars have not been initialized -- Do that */ PyCodeObject *co = frame->f_code; - PyObject *closure = frame->f_func->func_closure; + PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; int offset = co->co_nlocals + co->co_nplaincellvars; for (int i = 0; i < co->co_nfreevars; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); diff --git a/Python/ceval.c b/Python/ceval.c index ac77ab8..b3a0a36 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -154,11 +154,12 @@ lltrace_instruction(_PyInterpreterFrame *frame, static void lltrace_resume_frame(_PyInterpreterFrame *frame) { - PyFunctionObject *f = frame->f_func; - if (f == NULL) { + PyObject *fobj = frame->f_funcobj; + if (fobj == NULL || !PyFunction_Check(fobj)) { printf("\nResuming frame."); return; } + PyFunctionObject *f = (PyFunctionObject *)fobj; PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyObject *name = f->func_qualname; @@ -2619,7 +2620,8 @@ handle_eval_breaker: TARGET(COPY_FREE_VARS) { /* Copy closure variables to free variables */ PyCodeObject *co = frame->f_code; - PyObject *closure = frame->f_func->func_closure; + assert(PyFunction_Check(frame->f_funcobj)); + PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; int offset = co->co_nlocals + co->co_nplaincellvars; assert(oparg == co->co_nfreevars); for (int i = 0; i < oparg; ++i) { @@ -4897,7 +4899,9 @@ handle_eval_breaker: } TARGET(RETURN_GENERATOR) { - PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(frame->f_func); + assert(PyFunction_Check(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); if (gen == NULL) { goto error; } @@ -4919,7 +4923,7 @@ handle_eval_breaker: /* Make sure that frame is in a valid state */ frame->stacktop = 0; frame->f_locals = NULL; - Py_INCREF(frame->f_func); + Py_INCREF(frame->f_funcobj); Py_INCREF(frame->f_code); /* Restore previous cframe and return. */ tstate->cframe = cframe.previous; diff --git a/Python/frame.c b/Python/frame.c index 7c6705e..14464df 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -13,7 +13,7 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg) { Py_VISIT(frame->frame_obj); Py_VISIT(frame->f_locals); - Py_VISIT(frame->f_func); + Py_VISIT(frame->f_funcobj); Py_VISIT(frame->f_code); /* locals */ PyObject **locals = _PyFrame_GetLocalsArray(frame); @@ -114,7 +114,7 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) } Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->f_locals); - Py_DECREF(frame->f_func); + Py_DECREF(frame->f_funcobj); Py_DECREF(frame->f_code); } -- cgit v0.12