summaryrefslogtreecommitdiffstats
path: root/Python/frame.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-07-26 10:22:16 (GMT)
committerGitHub <noreply@github.com>2021-07-26 10:22:16 (GMT)
commitae0a2b756255629140efcbe57fc2e714f0267aa3 (patch)
tree8710e8c7a398c9ec0add227fab607f367242a7e5 /Python/frame.c
parent0363a4014d90df17a29042de008ef0b659f92505 (diff)
downloadcpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.zip
cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.gz
cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.bz2
bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it. * Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector. * Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation. * Move ownership of InterpreterFrame in generator from frame object to generator object. * Do not create frame objects for Python calls. * Do not create frame objects for generators.
Diffstat (limited to 'Python/frame.c')
-rw-r--r--Python/frame.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/Python/frame.c b/Python/frame.c
new file mode 100644
index 0000000..ae42843
--- /dev/null
+++ b/Python/frame.c
@@ -0,0 +1,135 @@
+
+#include "Python.h"
+#include "frameobject.h"
+#include "pycore_frame.h"
+#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+
+int
+_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg)
+{
+ Py_VISIT(frame->frame_obj);
+ Py_VISIT(frame->f_globals);
+ Py_VISIT(frame->f_builtins);
+ Py_VISIT(frame->f_locals);
+ Py_VISIT(frame->f_code);
+ /* locals */
+ PyObject **locals = _PyFrame_GetLocalsArray(frame);
+ for (int i = 0; i < frame->nlocalsplus; i++) {
+ Py_VISIT(locals[i]);
+ }
+ /* stack */
+ for (int i = 0; i <frame->stackdepth; i++) {
+ Py_VISIT(frame->stack[i]);
+ }
+ return 0;
+}
+
+PyFrameObject *
+_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame)
+{
+ assert(frame->frame_obj == NULL);
+ PyObject *error_type, *error_value, *error_traceback;
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0);
+ if (f == NULL) {
+ Py_XDECREF(error_type);
+ Py_XDECREF(error_value);
+ Py_XDECREF(error_traceback);
+ }
+ else {
+ PyErr_Restore(error_type, error_value, error_traceback);
+ }
+ frame->frame_obj = f;
+ return f;
+}
+
+
+static InterpreterFrame *
+copy_frame_to_heap(InterpreterFrame *frame)
+{
+
+ Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame);
+ PyObject **copy = PyMem_Malloc(size);
+ if (copy == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ PyObject **locals = _PyFrame_GetLocalsArray(frame);
+ memcpy(copy, locals, size);
+ InterpreterFrame *res = (InterpreterFrame *)(copy + frame->nlocalsplus);
+ return res;
+}
+
+static inline void
+clear_specials(InterpreterFrame *frame)
+{
+ frame->generator = NULL;
+ Py_XDECREF(frame->frame_obj);
+ Py_XDECREF(frame->f_locals);
+ Py_DECREF(frame->f_globals);
+ Py_DECREF(frame->f_builtins);
+ Py_DECREF(frame->f_code);
+}
+
+static void
+take_ownership(PyFrameObject *f, InterpreterFrame *frame)
+{
+ assert(f->f_own_locals_memory == 0);
+ assert(frame->frame_obj == NULL);
+
+ f->f_own_locals_memory = 1;
+ f->f_frame = frame;
+ assert(f->f_back == NULL);
+ if (frame->previous != NULL) {
+ /* Link PyFrameObjects.f_back and remove link through InterpreterFrame.previous */
+ PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous);
+ if (back == NULL) {
+ /* Memory error here. */
+ assert(PyErr_ExceptionMatches(PyExc_MemoryError));
+ /* Nothing we can do about it */
+ PyErr_Clear();
+ _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL);
+ }
+ else {
+ f->f_back = (PyFrameObject *)Py_NewRef(back);
+ }
+ frame->previous = NULL;
+ }
+ if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) {
+ _PyObject_GC_TRACK((PyObject *)f);
+ }
+}
+
+int
+_PyFrame_Clear(InterpreterFrame * frame, int take)
+{
+ PyObject **localsarray = ((PyObject **)frame)-frame->nlocalsplus;
+ if (frame->frame_obj) {
+ PyFrameObject *f = frame->frame_obj;
+ frame->frame_obj = NULL;
+ if (Py_REFCNT(f) > 1) {
+ if (!take) {
+ frame = copy_frame_to_heap(frame);
+ if (frame == NULL) {
+ return -1;
+ }
+ }
+ take_ownership(f, frame);
+ Py_DECREF(f);
+ return 0;
+ }
+ Py_DECREF(f);
+ }
+ for (int i = 0; i < frame->nlocalsplus; i++) {
+ Py_XDECREF(localsarray[i]);
+ }
+ assert(frame->stackdepth >= 0);
+ for (int i = 0; i < frame->stackdepth; i++) {
+ Py_DECREF(frame->stack[i]);
+ }
+ clear_specials(frame);
+ if (take) {
+ PyMem_Free(_PyFrame_GetLocalsArray(frame));
+ }
+ return 0;
+}