void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest);
-/* Consumes reference to func */
+/* Consumes reference to func and locals.
+ Does not initialize frame->previous, which happens
+ when frame is linked into the frame stack.
+ */
static inline void
_PyFrame_InitializeSpecials(
_PyInterpreterFrame *frame, PyFunctionObject *func,
frame = next(gen)
self.assertIs(gen, _testcapi.frame_getgenerator(frame))
+ def test_frame_fback_api(self):
+ """Test that accessing `f_back` does not cause a segmentation fault on
+ a frame created with `PyFrame_New` (GH-99110)."""
+ def dummy():
+ pass
+
+ frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
+ # The following line should not cause a segmentation fault.
+ self.assertIsNone(frame.f_back)
+
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
#include "Python.h"
#include "datetime.h" // PyDateTimeAPI
+#include "frameobject.h" // PyFrame_New
#include "marshal.h" // PyMarshal_WriteLongToFile
#include "structmember.h" // PyMemberDef
#include <float.h> // FLT_MAX
return PyLong_FromLong(lasti);
}
+static PyObject *
+frame_new(PyObject *self, PyObject *args)
+{
+ PyObject *code, *globals, *locals;
+ if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
+ return NULL;
+ }
+ if (!PyCode_Check(code)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a code object");
+ return NULL;
+ }
+ PyThreadState *tstate = PyThreadState_Get();
+
+ return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
+}
+
static PyObject *
eval_get_func_name(PyObject *self, PyObject *func)
{
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
{"frame_getlasti", frame_getlasti, METH_O, NULL},
+ {"frame_new", frame_new, METH_VARARGS, NULL},
{"eval_get_func_name", eval_get_func_name, METH_O, NULL},
{"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},