| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -132,6 +132,7 @@ | |||
| 132 | 132 | import unittest | |
| 133 | 133 | import textwrap | |
| 134 | 134 | import weakref | |
| 135 | + import dis | ||
| 135 | 136 | ||
| 136 | 137 | try: | |
| 137 | 138 | import ctypes | |
@@ -671,6 +672,38 @@ def test_lines(self): | |||
| 671 | 672 | self.check_lines(misshappen) | |
| 672 | 673 | self.check_lines(bug93662) | |
| 673 | 674 | ||
| 675 | + @cpython_only | ||
| 676 | + def test_code_new_empty(self): | ||
| 677 | + # If this test fails, it means that the construction of PyCode_NewEmpty | ||
| 678 | + # needs to be modified! Please update this test *and* PyCode_NewEmpty, | ||
| 679 | + # so that they both stay in sync. | ||
| 680 | + def f(): | ||
| 681 | + pass | ||
| 682 | + PY_CODE_LOCATION_INFO_NO_COLUMNS = 13 | ||
| 683 | + f.__code__ = f.__code__.replace( | ||
| 684 | + co_firstlineno=42, | ||
| 685 | + co_code=bytes( | ||
| 686 | + [ | ||
| 687 | + dis.opmap["RESUME"], 0, | ||
| 688 | + dis.opmap["LOAD_ASSERTION_ERROR"], 0, | ||
| 689 | + dis.opmap["RAISE_VARARGS"], 1, | ||
| 690 | + ] | ||
| 691 | + ), | ||
| 692 | + co_linetable=bytes( | ||
| 693 | + [ | ||
| 694 | + (1 << 7) | ||
| 695 | + | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) | ||
| 696 | + | (3 - 1), | ||
| 697 | + 0, | ||
| 698 | + ] | ||
| 699 | + ), | ||
| 700 | + ) | ||
| 701 | + self.assertRaises(AssertionError, f) | ||
| 702 | + self.assertEqual( | ||
| 703 | + list(f.__code__.co_positions()), | ||
| 704 | + 3 * [(42, 42, None, None)], | ||
| 705 | + ) | ||
| 706 | + | ||
| 674 | 707 | ||
| 675 | 708 | if check_impl_detail(cpython=True) and ctypes is not None: | |
| 676 | 709 | py = ctypes.pythonapi | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + Ensure that all Python frame objects are backed by "complete" frames. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -636,19 +636,30 @@ PyCode_New(int argcount, int kwonlyargcount, | |||
| 636 | 636 | exceptiontable); | |
| 637 | 637 | } | |
| 638 | 638 | ||
| 639 | - static const char assert0[6] = { | ||
| 639 | + // NOTE: When modifying the construction of PyCode_NewEmpty, please also change | ||
| 640 | + // test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync! | ||
| 641 | + | ||
| 642 | + static const uint8_t assert0[6] = { | ||
| 640 | 643 | RESUME, 0, | |
| 641 | 644 | LOAD_ASSERTION_ERROR, 0, | |
| 642 | 645 | RAISE_VARARGS, 1 | |
| 643 | 646 | }; | |
| 644 | 647 | ||
| 648 | + static const uint8_t linetable[2] = { | ||
| 649 | + (1 << 7) // New entry. | ||
| 650 | + | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) | ||
| 651 | + | (3 - 1), // Three code units. | ||
| 652 | + 0, // Offset from co_firstlineno. | ||
| 653 | + }; | ||
| 654 | + | ||
| 645 | 655 | PyCodeObject * | |
| 646 | 656 | PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) | |
| 647 | 657 | { | |
| 648 | 658 | PyObject *nulltuple = NULL; | |
| 649 | 659 | PyObject *filename_ob = NULL; | |
| 650 | 660 | PyObject *funcname_ob = NULL; | |
| 651 | 661 | PyObject *code_ob = NULL; | |
| 662 | + PyObject *linetable_ob = NULL; | ||
| 652 | 663 | PyCodeObject *result = NULL; | |
| 653 | 664 | ||
| 654 | 665 | nulltuple = PyTuple_New(0); | |
@@ -663,10 +674,14 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) | |||
| 663 | 674 | if (filename_ob == NULL) { | |
| 664 | 675 | goto failed; | |
| 665 | 676 | } | |
| 666 | - code_ob = PyBytes_FromStringAndSize(assert0, 6); | ||
| 677 | + code_ob = PyBytes_FromStringAndSize((const char *)assert0, 6); | ||
| 667 | 678 | if (code_ob == NULL) { | |
| 668 | 679 | goto failed; | |
| 669 | 680 | } | |
| 681 | + linetable_ob = PyBytes_FromStringAndSize((const char *)linetable, 2); | ||
| 682 | + if (linetable_ob == NULL) { | ||
| 683 | + goto failed; | ||
| 684 | + } | ||
| 670 | 685 | ||
| 671 | 686 | #define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty) | |
| 672 | 687 | struct _PyCodeConstructor con = { | |
@@ -675,7 +690,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) | |||
| 675 | 690 | .qualname = funcname_ob, | |
| 676 | 691 | .code = code_ob, | |
| 677 | 692 | .firstlineno = firstlineno, | |
| 678 | - .linetable = emptystring, | ||
| 693 | + .linetable = linetable_ob, | ||
| 679 | 694 | .consts = nulltuple, | |
| 680 | 695 | .names = nulltuple, | |
| 681 | 696 | .localsplusnames = nulltuple, | |
@@ -690,6 +705,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) | |||
| 690 | 705 | Py_XDECREF(funcname_ob); | |
| 691 | 706 | Py_XDECREF(filename_ob); | |
| 692 | 707 | Py_XDECREF(code_ob); | |
| 708 | + Py_XDECREF(linetable_ob); | ||
| 693 | 709 | return result; | |
| 694 | 710 | } | |
| 695 | 711 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -590,6 +590,7 @@ first_line_not_before(int *lines, int len, int line) | |||
| 590 | 590 | static PyFrameState | |
| 591 | 591 | _PyFrame_GetState(PyFrameObject *frame) | |
| 592 | 592 | { | |
| 593 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 593 | 594 | if (frame->f_frame->stacktop == 0) { | |
| 594 | 595 | return FRAME_CLEARED; | |
| 595 | 596 | } | |
@@ -1063,6 +1064,9 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, | |||
| 1063 | 1064 | init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals); | |
| 1064 | 1065 | f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data; | |
| 1065 | 1066 | f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; | |
| 1067 | + // This frame needs to be "complete", so pretend that the first RESUME ran: | ||
| 1068 | + f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable; | ||
| 1069 | + assert(!_PyFrame_IsIncomplete(f->f_frame)); | ||
| 1066 | 1070 | Py_DECREF(func); | |
| 1067 | 1071 | _PyObject_GC_TRACK(f); | |
| 1068 | 1072 | return f; | |
@@ -1189,6 +1193,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { | |||
| 1189 | 1193 | int | |
| 1190 | 1194 | PyFrame_FastToLocalsWithError(PyFrameObject *f) | |
| 1191 | 1195 | { | |
| 1196 | + assert(!_PyFrame_IsIncomplete(f->f_frame)); | ||
| 1192 | 1197 | if (f == NULL) { | |
| 1193 | 1198 | PyErr_BadInternalCall(); | |
| 1194 | 1199 | return -1; | |
@@ -1204,7 +1209,7 @@ void | |||
| 1204 | 1209 | PyFrame_FastToLocals(PyFrameObject *f) | |
| 1205 | 1210 | { | |
| 1206 | 1211 | int res; | |
| 1207 | - | ||
| 1212 | + assert(!_PyFrame_IsIncomplete(f->f_frame)); | ||
| 1208 | 1213 | assert(!PyErr_Occurred()); | |
| 1209 | 1214 | ||
| 1210 | 1215 | res = PyFrame_FastToLocalsWithError(f); | |
@@ -1282,6 +1287,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) | |||
| 1282 | 1287 | void | |
| 1283 | 1288 | PyFrame_LocalsToFast(PyFrameObject *f, int clear) | |
| 1284 | 1289 | { | |
| 1290 | + assert(!_PyFrame_IsIncomplete(f->f_frame)); | ||
| 1285 | 1291 | if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) { | |
| 1286 | 1292 | _PyFrame_LocalsToFast(f->f_frame, clear); | |
| 1287 | 1293 | f->f_fast_as_locals = 0; | |
@@ -1292,6 +1298,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) | |||
| 1292 | 1298 | int _PyFrame_IsEntryFrame(PyFrameObject *frame) | |
| 1293 | 1299 | { | |
| 1294 | 1300 | assert(frame != NULL); | |
| 1301 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1295 | 1302 | return frame->f_frame->is_entry; | |
| 1296 | 1303 | } | |
| 1297 | 1304 | ||
@@ -1300,6 +1307,7 @@ PyCodeObject * | |||
| 1300 | 1307 | PyFrame_GetCode(PyFrameObject *frame) | |
| 1301 | 1308 | { | |
| 1302 | 1309 | assert(frame != NULL); | |
| 1310 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1303 | 1311 | PyCodeObject *code = frame->f_frame->f_code; | |
| 1304 | 1312 | assert(code != NULL); | |
| 1305 | 1313 | Py_INCREF(code); | |
@@ -1311,6 +1319,7 @@ PyFrameObject* | |||
| 1311 | 1319 | PyFrame_GetBack(PyFrameObject *frame) | |
| 1312 | 1320 | { | |
| 1313 | 1321 | assert(frame != NULL); | |
| 1322 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1314 | 1323 | PyFrameObject *back = frame->f_back; | |
| 1315 | 1324 | if (back == NULL) { | |
| 1316 | 1325 | _PyInterpreterFrame *prev = frame->f_frame->previous; | |
@@ -1328,24 +1337,28 @@ PyFrame_GetBack(PyFrameObject *frame) | |||
| 1328 | 1337 | PyObject* | |
| 1329 | 1338 | PyFrame_GetLocals(PyFrameObject *frame) | |
| 1330 | 1339 | { | |
| 1340 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1331 | 1341 | return frame_getlocals(frame, NULL); | |
| 1332 | 1342 | } | |
| 1333 | 1343 | ||
| 1334 | 1344 | PyObject* | |
| 1335 | 1345 | PyFrame_GetGlobals(PyFrameObject *frame) | |
| 1336 | 1346 | { | |
| 1347 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1337 | 1348 | return frame_getglobals(frame, NULL); | |
| 1338 | 1349 | } | |
| 1339 | 1350 | ||
| 1340 | 1351 | PyObject* | |
| 1341 | 1352 | PyFrame_GetBuiltins(PyFrameObject *frame) | |
| 1342 | 1353 | { | |
| 1354 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1343 | 1355 | return frame_getbuiltins(frame, NULL); | |
| 1344 | 1356 | } | |
| 1345 | 1357 | ||
| 1346 | 1358 | int | |
| 1347 | 1359 | PyFrame_GetLasti(PyFrameObject *frame) | |
| 1348 | 1360 | { | |
| 1361 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1349 | 1362 | int lasti = _PyInterpreterFrame_LASTI(frame->f_frame); | |
| 1350 | 1363 | if (lasti < 0) { | |
| 1351 | 1364 | return -1; | |
@@ -1356,6 +1369,7 @@ PyFrame_GetLasti(PyFrameObject *frame) | |||
| 1356 | 1369 | PyObject * | |
| 1357 | 1370 | PyFrame_GetGenerator(PyFrameObject *frame) | |
| 1358 | 1371 | { | |
| 1372 | + assert(!_PyFrame_IsIncomplete(frame->f_frame)); | ||
| 1359 | 1373 | if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { | |
| 1360 | 1374 | return NULL; | |
| 1361 | 1375 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,6 +68,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) | |||
| 68 | 68 | frame = (_PyInterpreterFrame *)f->_f_frame_data; | |
| 69 | 69 | f->f_frame = frame; | |
| 70 | 70 | frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; | |
| 71 | + if (_PyFrame_IsIncomplete(frame)) { | ||
| 72 | + // This may be a newly-created generator or coroutine frame. Since it's | ||
| 73 | + // dead anyways, just pretend that the first RESUME ran: | ||
| 74 | + PyCodeObject *code = frame->f_code; | ||
| 75 | + frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable; | ||
| 76 | + } | ||
| 77 | + assert(!_PyFrame_IsIncomplete(frame)); | ||
| 71 | 78 | assert(f->f_back == NULL); | |
| 72 | 79 | _PyInterpreterFrame *prev = frame->previous; | |
| 73 | 80 | while (prev && _PyFrame_IsIncomplete(prev)) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments