Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0ff8fd6

Browse files
authored
GH-97779: Ensure that *all* frame objects are backed by "complete" frames (GH-97845)
1 parent c3648f4 commit 0ff8fd6

File tree

5 files changed

+75
-4
lines changed

5 files changed

+75
-4
lines changed

Lib/test/test_code.py

+33
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import unittest
133133
import textwrap
134134
import weakref
135+
import dis
135136

136137
try:
137138
import ctypes
@@ -682,6 +683,38 @@ def test_lines(self):
682683
self.check_lines(misshappen)
683684
self.check_lines(bug93662)
684685

686+
@cpython_only
687+
def test_code_new_empty(self):
688+
# If this test fails, it means that the construction of PyCode_NewEmpty
689+
# needs to be modified! Please update this test *and* PyCode_NewEmpty,
690+
# so that they both stay in sync.
691+
def f():
692+
pass
693+
PY_CODE_LOCATION_INFO_NO_COLUMNS = 13
694+
f.__code__ = f.__code__.replace(
695+
co_firstlineno=42,
696+
co_code=bytes(
697+
[
698+
dis.opmap["RESUME"], 0,
699+
dis.opmap["LOAD_ASSERTION_ERROR"], 0,
700+
dis.opmap["RAISE_VARARGS"], 1,
701+
]
702+
),
703+
co_linetable=bytes(
704+
[
705+
(1 << 7)
706+
| (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3)
707+
| (3 - 1),
708+
0,
709+
]
710+
),
711+
)
712+
self.assertRaises(AssertionError, f)
713+
self.assertEqual(
714+
list(f.__code__.co_positions()),
715+
3 * [(42, 42, None, None)],
716+
)
717+
685718

686719
if check_impl_detail(cpython=True) and ctypes is not None:
687720
py = ctypes.pythonapi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure that all Python frame objects are backed by "complete" frames.

Objects/codeobject.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -638,19 +638,30 @@ PyCode_New(int argcount, int kwonlyargcount,
638638
exceptiontable);
639639
}
640640

641-
static const char assert0[6] = {
641+
// NOTE: When modifying the construction of PyCode_NewEmpty, please also change
642+
// test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync!
643+
644+
static const uint8_t assert0[6] = {
642645
RESUME, 0,
643646
LOAD_ASSERTION_ERROR, 0,
644647
RAISE_VARARGS, 1
645648
};
646649

650+
static const uint8_t linetable[2] = {
651+
(1 << 7) // New entry.
652+
| (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3)
653+
| (3 - 1), // Three code units.
654+
0, // Offset from co_firstlineno.
655+
};
656+
647657
PyCodeObject *
648658
PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
649659
{
650660
PyObject *nulltuple = NULL;
651661
PyObject *filename_ob = NULL;
652662
PyObject *funcname_ob = NULL;
653663
PyObject *code_ob = NULL;
664+
PyObject *linetable_ob = NULL;
654665
PyCodeObject *result = NULL;
655666

656667
nulltuple = PyTuple_New(0);
@@ -665,10 +676,14 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
665676
if (filename_ob == NULL) {
666677
goto failed;
667678
}
668-
code_ob = PyBytes_FromStringAndSize(assert0, 6);
679+
code_ob = PyBytes_FromStringAndSize((const char *)assert0, 6);
669680
if (code_ob == NULL) {
670681
goto failed;
671682
}
683+
linetable_ob = PyBytes_FromStringAndSize((const char *)linetable, 2);
684+
if (linetable_ob == NULL) {
685+
goto failed;
686+
}
672687

673688
#define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty)
674689
struct _PyCodeConstructor con = {
@@ -677,7 +692,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
677692
.qualname = funcname_ob,
678693
.code = code_ob,
679694
.firstlineno = firstlineno,
680-
.linetable = emptystring,
695+
.linetable = linetable_ob,
681696
.consts = nulltuple,
682697
.names = nulltuple,
683698
.localsplusnames = nulltuple,
@@ -692,6 +707,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
692707
Py_XDECREF(funcname_ob);
693708
Py_XDECREF(filename_ob);
694709
Py_XDECREF(code_ob);
710+
Py_XDECREF(linetable_ob);
695711
return result;
696712
}
697713

Objects/frameobject.c

+15-1
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ first_line_not_before(int *lines, int len, int line)
588588
static PyFrameState
589589
_PyFrame_GetState(PyFrameObject *frame)
590590
{
591+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
591592
if (frame->f_frame->stacktop == 0) {
592593
return FRAME_CLEARED;
593594
}
@@ -1094,6 +1095,9 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
10941095
init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals);
10951096
f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data;
10961097
f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
1098+
// This frame needs to be "complete", so pretend that the first RESUME ran:
1099+
f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
1100+
assert(!_PyFrame_IsIncomplete(f->f_frame));
10971101
Py_DECREF(func);
10981102
_PyObject_GC_TRACK(f);
10991103
return f;
@@ -1222,6 +1226,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
12221226
int
12231227
PyFrame_FastToLocalsWithError(PyFrameObject *f)
12241228
{
1229+
assert(!_PyFrame_IsIncomplete(f->f_frame));
12251230
if (f == NULL) {
12261231
PyErr_BadInternalCall();
12271232
return -1;
@@ -1237,7 +1242,7 @@ void
12371242
PyFrame_FastToLocals(PyFrameObject *f)
12381243
{
12391244
int res;
1240-
1245+
assert(!_PyFrame_IsIncomplete(f->f_frame));
12411246
assert(!PyErr_Occurred());
12421247

12431248
res = PyFrame_FastToLocalsWithError(f);
@@ -1320,6 +1325,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
13201325
void
13211326
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
13221327
{
1328+
assert(!_PyFrame_IsIncomplete(f->f_frame));
13231329
if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) {
13241330
_PyFrame_LocalsToFast(f->f_frame, clear);
13251331
f->f_fast_as_locals = 0;
@@ -1330,6 +1336,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
13301336
int _PyFrame_IsEntryFrame(PyFrameObject *frame)
13311337
{
13321338
assert(frame != NULL);
1339+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13331340
return frame->f_frame->is_entry;
13341341
}
13351342

@@ -1338,6 +1345,7 @@ PyCodeObject *
13381345
PyFrame_GetCode(PyFrameObject *frame)
13391346
{
13401347
assert(frame != NULL);
1348+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13411349
PyCodeObject *code = frame->f_frame->f_code;
13421350
assert(code != NULL);
13431351
Py_INCREF(code);
@@ -1349,6 +1357,7 @@ PyFrameObject*
13491357
PyFrame_GetBack(PyFrameObject *frame)
13501358
{
13511359
assert(frame != NULL);
1360+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13521361
PyFrameObject *back = frame->f_back;
13531362
if (back == NULL) {
13541363
_PyInterpreterFrame *prev = frame->f_frame->previous;
@@ -1366,24 +1375,28 @@ PyFrame_GetBack(PyFrameObject *frame)
13661375
PyObject*
13671376
PyFrame_GetLocals(PyFrameObject *frame)
13681377
{
1378+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13691379
return frame_getlocals(frame, NULL);
13701380
}
13711381

13721382
PyObject*
13731383
PyFrame_GetGlobals(PyFrameObject *frame)
13741384
{
1385+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13751386
return frame_getglobals(frame, NULL);
13761387
}
13771388

13781389
PyObject*
13791390
PyFrame_GetBuiltins(PyFrameObject *frame)
13801391
{
1392+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13811393
return frame_getbuiltins(frame, NULL);
13821394
}
13831395

13841396
int
13851397
PyFrame_GetLasti(PyFrameObject *frame)
13861398
{
1399+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13871400
int lasti = _PyInterpreterFrame_LASTI(frame->f_frame);
13881401
if (lasti < 0) {
13891402
return -1;
@@ -1394,6 +1407,7 @@ PyFrame_GetLasti(PyFrameObject *frame)
13941407
PyObject *
13951408
PyFrame_GetGenerator(PyFrameObject *frame)
13961409
{
1410+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
13971411
if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) {
13981412
return NULL;
13991413
}

Python/frame.c

+7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
7070
frame = (_PyInterpreterFrame *)f->_f_frame_data;
7171
f->f_frame = frame;
7272
frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
73+
if (_PyFrame_IsIncomplete(frame)) {
74+
// This may be a newly-created generator or coroutine frame. Since it's
75+
// dead anyways, just pretend that the first RESUME ran:
76+
PyCodeObject *code = frame->f_code;
77+
frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
78+
}
79+
assert(!_PyFrame_IsIncomplete(frame));
7380
assert(f->f_back == NULL);
7481
_PyInterpreterFrame *prev = frame->previous;
7582
while (prev && _PyFrame_IsIncomplete(prev)) {

0 commit comments

Comments
 (0)