Skip to content

Commit 5a4803c

Browse files
committed
Move globals and builtins from frame object to per-thread stack.
1 parent 8a9ae01 commit 5a4803c

File tree

9 files changed

+152
-83
lines changed

9 files changed

+152
-83
lines changed

Include/cpython/frameobject.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ enum _framestate {
1919

2020
typedef signed char PyFrameState;
2121

22+
enum {
23+
FRAME_SPECIALS_GLOBALS_OFFSET = 0,
24+
FRAME_SPECIALS_BUILTINS_OFFSET = 1,
25+
FRAME_SPECIALS_SIZE = 2
26+
};
27+
2228
struct _frame {
2329
PyObject_HEAD
2430
struct _frame *f_back; /* previous frame, or NULL */
2531
PyCodeObject *f_code; /* code segment */
26-
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
27-
PyObject *f_globals; /* global symbol table (PyDictObject) */
2832
PyObject *f_locals; /* local symbol table (any mapping) */
2933
PyObject **f_valuestack; /* points after the last local */
3034
PyObject *f_trace; /* Trace function */
@@ -79,4 +83,7 @@ PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);
7983

8084
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
8185

82-
int _PyFrame_MakeCopyOfLocals(PyFrameObject *f);
86+
int _PyFrame_StealLocals(PyFrameObject *f);
87+
88+
PyObject *_PyFrame_GetGlobals(PyFrameObject *f);
89+
PyObject *_PyFrame_GetBuiltins(PyFrameObject *f);

Include/genobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extern "C" {
1818
/* Note: gi_frame can be NULL if the generator is "finished" */ \
1919
PyFrameObject *prefix##_frame; \
2020
/* The code object backing the generator */ \
21-
PyObject *prefix##_code; \
21+
PyCodeObject *prefix##_code; \
2222
/* List of weak reference. */ \
2323
PyObject *prefix##_weakreflist; \
2424
/* Name of the generator. */ \

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ class C(object): pass
12741274
# frame
12751275
import inspect
12761276
x = inspect.currentframe()
1277-
check(x, size('8P3i4cP'))
1277+
check(x, size('6P3i4cP'))
12781278
# function
12791279
def func(): pass
12801280
check(func, size('14P'))

Objects/frameobject.c

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313

1414
static PyMemberDef frame_memberlist[] = {
1515
{"f_back", T_OBJECT, OFF(f_back), READONLY},
16-
{"f_code", T_OBJECT, OFF(f_code), READONLY|PY_AUDIT_READ},
17-
{"f_builtins", T_OBJECT, OFF(f_builtins), READONLY},
18-
{"f_globals", T_OBJECT, OFF(f_globals), READONLY},
1916
{"f_trace_lines", T_BOOL, OFF(f_trace_lines), 0},
2017
{"f_trace_opcodes", T_BOOL, OFF(f_trace_opcodes), 0},
2118
{NULL} /* Sentinel */
@@ -38,6 +35,11 @@ frame_getlocals(PyFrameObject *f, void *closure)
3835
return f->f_locals;
3936
}
4037

38+
static inline PyObject **
39+
_PyFrame_Specials(PyFrameObject *f) {
40+
return &f->f_valuestack[-FRAME_SPECIALS_SIZE];
41+
}
42+
4143
int
4244
PyFrame_GetLineNumber(PyFrameObject *f)
4345
{
@@ -71,6 +73,50 @@ frame_getlasti(PyFrameObject *f, void *closure)
7173
return PyLong_FromLong(f->f_lasti*2);
7274
}
7375

76+
/* Returns a *borrowed* reference. Not part of the API. */
77+
PyObject *
78+
_PyFrame_GetGlobals(PyFrameObject *f)
79+
{
80+
return _PyFrame_Specials(f)[FRAME_SPECIALS_GLOBALS_OFFSET];
81+
}
82+
83+
/* Returns a *borrowed* reference. Not part of the API. */
84+
PyObject *
85+
_PyFrame_GetBuiltins(PyFrameObject *f)
86+
{
87+
return _PyFrame_Specials(f)[FRAME_SPECIALS_BUILTINS_OFFSET];
88+
}
89+
90+
static PyObject *
91+
frame_getglobals(PyFrameObject *f, void *closure)
92+
{
93+
PyObject *globals = _PyFrame_GetGlobals(f);
94+
if (globals == NULL) {
95+
globals = Py_None;
96+
}
97+
Py_INCREF(globals);
98+
return globals;
99+
}
100+
101+
static PyObject *
102+
frame_getbuiltins(PyFrameObject *f, void *closure)
103+
{
104+
PyObject *builtins = _PyFrame_GetBuiltins(f);
105+
if (builtins == NULL) {
106+
builtins = Py_None;
107+
}
108+
Py_INCREF(builtins);
109+
return builtins;
110+
}
111+
112+
static PyObject *
113+
frame_getcode(PyFrameObject *f, void *closure)
114+
{
115+
if (PySys_Audit("object.__getattr__", "Os", f, "f_code") < 0) {
116+
return NULL;
117+
}
118+
return (PyObject *)PyFrame_GetCode(f);
119+
}
74120

75121
/* Given the index of the effective opcode,
76122
scan back to construct the oparg with EXTENDED_ARG */
@@ -554,6 +600,9 @@ static PyGetSetDef frame_getsetlist[] = {
554600
(setter)frame_setlineno, NULL},
555601
{"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL},
556602
{"f_lasti", (getter)frame_getlasti, NULL, NULL},
603+
{"f_globals", (getter)frame_getglobals, NULL, NULL},
604+
{"f_builtins", (getter)frame_getbuiltins, NULL, NULL},
605+
{"f_code", (getter)frame_getcode, NULL, NULL},
557606
{0}
558607
};
559608

@@ -581,7 +630,7 @@ frame_dealloc(PyFrameObject *f)
581630

582631
/* Kill all local variables */
583632
if (f->f_own_locals_memory) {
584-
for (int i = 0; i < co->co_nlocalsplus; i++) {
633+
for (int i = 0; i < co->co_nlocalsplus+FRAME_SPECIALS_SIZE; i++) {
585634
Py_CLEAR(f->f_localsptr[i]);
586635
}
587636
/* Free items on stack */
@@ -593,8 +642,6 @@ frame_dealloc(PyFrameObject *f)
593642
}
594643
f->f_stackdepth = 0;
595644
Py_XDECREF(f->f_back);
596-
Py_DECREF(f->f_builtins);
597-
Py_DECREF(f->f_globals);
598645
Py_CLEAR(f->f_locals);
599646
Py_CLEAR(f->f_trace);
600647
struct _Py_frame_state *state = get_frame_state();
@@ -618,17 +665,13 @@ frame_dealloc(PyFrameObject *f)
618665
static inline Py_ssize_t
619666
frame_nslots(PyFrameObject *frame)
620667
{
621-
PyCodeObject *code = frame->f_code;
622-
return code->co_nlocalsplus;
668+
return frame->f_valuestack - frame->f_localsptr;
623669
}
624670

625671
static int
626672
frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
627673
{
628674
Py_VISIT(f->f_back);
629-
Py_VISIT(f->f_code);
630-
Py_VISIT(f->f_builtins);
631-
Py_VISIT(f->f_globals);
632675
Py_VISIT(f->f_locals);
633676
Py_VISIT(f->f_trace);
634677

@@ -766,7 +809,8 @@ frame_alloc(PyCodeObject *code, PyObject **localsarray)
766809
int owns;
767810
PyFrameObject *f;
768811
if (localsarray == NULL) {
769-
localsarray = PyMem_Malloc(sizeof(PyObject *)*(code->co_nlocalsplus+code->co_stacksize));
812+
int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE;
813+
localsarray = PyMem_Malloc(sizeof(PyObject *)*size);
770814
if (localsarray == NULL) {
771815
PyErr_NoMemory();
772816
return NULL;
@@ -803,29 +847,32 @@ frame_alloc(PyCodeObject *code, PyObject **localsarray)
803847
}
804848
f->f_localsptr = localsarray;
805849
f->f_own_locals_memory = owns;
806-
f->f_valuestack = f->f_localsptr + code->co_nlocalsplus;
850+
f->f_valuestack = f->f_localsptr + code->co_nlocalsplus + FRAME_SPECIALS_SIZE;
807851
return f;
808852
}
809853

810854
int
811-
_PyFrame_MakeCopyOfLocals(PyFrameObject *f)
855+
_PyFrame_StealLocals(PyFrameObject *f)
812856
{
813-
if (f->f_own_locals_memory) {
814-
return 0;
815-
}
816-
PyObject **copy = PyMem_Malloc(sizeof(PyObject *)*(f->f_code->co_nlocalsplus+f->f_code->co_stacksize));
857+
assert(f->f_own_locals_memory == 0);
858+
assert(f->f_stackdepth == 0);
859+
int size = frame_nslots(f);
860+
PyObject **copy = PyMem_Malloc(sizeof(PyObject *)*size);
817861
if (copy == NULL) {
862+
for (int i = 0; i < size; i++) {
863+
PyObject *o = f->f_localsptr[i];
864+
Py_XDECREF(o);
865+
}
818866
PyErr_NoMemory();
819867
return -1;
820868
}
821-
for (int i = 0; i < f->f_code->co_nlocalsplus+f->f_stackdepth; i++) {
869+
for (int i = 0; i < size; i++) {
822870
PyObject *o = f->f_localsptr[i];
823-
Py_XINCREF(o);
824871
copy[i] = o;
825872
}
826873
f->f_own_locals_memory = 1;
827874
f->f_localsptr = copy;
828-
f->f_valuestack = f->f_localsptr + f->f_code->co_nlocalsplus;
875+
f->f_valuestack = copy + size;
829876
return 0;
830877
}
831878

@@ -845,8 +892,8 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyFrameConstructor *con, PyObject *l
845892

846893
f->f_back = (PyFrameObject*)Py_XNewRef(tstate->frame);
847894
f->f_code = (PyCodeObject *)Py_NewRef(con->fc_code);
848-
f->f_builtins = Py_NewRef(con->fc_builtins);
849-
f->f_globals = Py_NewRef(con->fc_globals);
895+
_PyFrame_Specials(f)[FRAME_SPECIALS_BUILTINS_OFFSET] = Py_NewRef(con->fc_builtins);
896+
_PyFrame_Specials(f)[FRAME_SPECIALS_GLOBALS_OFFSET] = Py_NewRef(con->fc_globals);
850897
f->f_locals = Py_XNewRef(locals);
851898
// f_valuestack initialized by frame_alloc()
852899
f->f_trace = NULL;

Objects/genobject.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
176176
}
177177

178178
assert(_PyFrame_IsRunnable(f));
179-
assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(f->f_code->co_code))[0] == GEN_START);
179+
assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START);
180180
/* Push arg onto the frame's value stack */
181181
result = arg ? arg : Py_None;
182182
Py_INCREF(result);
@@ -331,7 +331,7 @@ _PyGen_yf(PyGenObject *gen)
331331
PyFrameObject *f = gen->gi_frame;
332332

333333
if (f) {
334-
PyObject *bytecode = f->f_code->co_code;
334+
PyObject *bytecode = gen->gi_code->co_code;
335335
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
336336

337337
if (f->f_lasti < 0) {
@@ -826,8 +826,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
826826
}
827827
gen->gi_frame = f;
828828
f->f_gen = (PyObject *) gen;
829-
Py_INCREF(f->f_code);
830-
gen->gi_code = (PyObject *)(f->f_code);
829+
gen->gi_code = PyFrame_GetCode(f);
831830
gen->gi_weakreflist = NULL;
832831
gen->gi_exc_state.exc_type = NULL;
833832
gen->gi_exc_state.exc_value = NULL;
@@ -836,7 +835,7 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
836835
if (name != NULL)
837836
gen->gi_name = name;
838837
else
839-
gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name;
838+
gen->gi_name = gen->gi_code->co_name;
840839
Py_INCREF(gen->gi_name);
841840
if (qualname != NULL)
842841
gen->gi_qualname = qualname;
@@ -1167,11 +1166,12 @@ compute_cr_origin(int origin_depth)
11671166
}
11681167
frame = PyEval_GetFrame();
11691168
for (int i = 0; i < frame_count; ++i) {
1170-
PyCodeObject *code = frame->f_code;
1169+
PyCodeObject *code = PyFrame_GetCode(frame);
11711170
PyObject *frameinfo = Py_BuildValue("OiO",
11721171
code->co_filename,
11731172
PyFrame_GetLineNumber(frame),
11741173
code->co_name);
1174+
Py_DECREF(code);
11751175
if (!frameinfo) {
11761176
Py_DECREF(cr_origin);
11771177
return NULL;

Python/_warnings.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
853853
*lineno = 1;
854854
}
855855
else {
856-
globals = f->f_globals;
856+
globals = _PyFrame_GetGlobals(f);
857857
PyCodeObject *code = PyFrame_GetCode(f);
858858
*filename = code->co_filename;
859859
Py_DECREF(code);

0 commit comments

Comments
 (0)