From 78cd7a3160214134e72e7aa28d20166409e41de9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 8 Apr 2022 11:21:00 +0100 Subject: [PATCH 01/18] Compact debug info. Work in progress. --- Include/cpython/code.h | 22 +- Include/internal/pycore_code.h | 18 +- Lib/importlib/_bootstrap_external.py | 1 + Lib/test/test_code.py | 157 ++++++++--- Lib/test/test_compile.py | 33 ++- Lib/test/test_dis.py | 23 +- Lib/test/test_exceptions.py | 2 +- Lib/test/test_traceback.py | 1 + Objects/clinic/codeobject.c.h | 61 +++-- Objects/codeobject.c | 386 ++++++++++++++++++--------- Programs/test_frozenmain.h | 12 +- Python/compile.c | 176 ++++++++++++ Python/marshal.c | 6 + Tools/scripts/deepfreeze.py | 2 + Tools/scripts/umarshal.py | 1 + 15 files changed, 673 insertions(+), 228 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 6dc2290ffeb5e2..0ee90d32c5eede 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -95,6 +95,7 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_columntable; /* bytes object that holds start/end column \ offset each instruction */ \ \ + PyObject *co_locationtable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ /* Scratch space for extra data relating to the code object. \ Type is a void* to keep the format private in codeobject.c to force \ @@ -153,13 +154,13 @@ PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *, PyObject *, PyObject *); /* same as struct above */ /* Creates a new empty code object with the specified source location. */ @@ -176,8 +177,8 @@ PyAPI_FUNC(int) PyCode_Addr2Location(PyCodeObject *, int, int *, int *, int *, i /* for internal use only */ struct _opaque { int computed_line; - const char *lo_next; - const char *limit; + const uint8_t *lo_next; + const uint8_t *limit; }; typedef struct _line_offsets { @@ -210,6 +211,19 @@ PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra); + +typedef enum _PyCodeLocationInfoKind { + PY_CODE_LOCATION_INFO_SHORT0 = 0, + PY_CODE_LOCATION_INFO_SHORT1 = 1, + PY_CODE_LOCATION_INFO_SHORT2 = 2, + PY_CODE_LOCATION_INFO_SHORT3 = 3, + PY_CODE_LOCATION_INFO_SHORT4 = 4, + PY_CODE_LOCATION_INFO_SHORT5 = 5, + + PYCODE_LOCATION_INFO_TWO_LINES = 14, + PYCODE_LOCATION_INFO_NONE = 15 +} _PyCodeLocationInfoKind; + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 8c868bcd5b5cbe..9d51d0ef189006 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -178,6 +178,7 @@ struct _PyCodeConstructor { PyObject *linetable; PyObject *endlinetable; PyObject *columntable; + PyObject *locationtable; /* used by the code */ PyObject *consts; @@ -221,23 +222,12 @@ extern PyObject* _PyCode_GetCellvars(PyCodeObject *); extern PyObject* _PyCode_GetFreevars(PyCodeObject *); extern PyObject* _PyCode_GetCode(PyCodeObject *); -/* Return the ending source code line number from a bytecode index. */ -extern int _PyCode_Addr2EndLine(PyCodeObject *, int); - -/* Return the ending source code line number from a bytecode index. */ -extern int _PyCode_Addr2EndLine(PyCodeObject *, int); -/* Return the starting source code column offset from a bytecode index. */ -extern int _PyCode_Addr2Offset(PyCodeObject *, int); -/* Return the ending source code column offset from a bytecode index. */ -extern int _PyCode_Addr2EndOffset(PyCodeObject *, int); - /** API for initializing the line number tables. */ extern int _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds); -extern int _PyCode_InitEndAddressRange(PyCodeObject* co, PyCodeAddressRange* bounds); -/** Out of process API for initializing the line number table. */ -extern void _PyLineTable_InitAddressRange( - const char *linetable, +/** Out of process API for initializing the location table. */ +extern void _PyLocationTable_InitAddressRange( + const char *locationtable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range); diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 45be177df76a92..50de4015e46989 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -400,6 +400,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.11a6 3490 (remove JUMP_IF_NOT_EXC_MATCH, add CHECK_EXC_MATCH) # Python 3.11a6 3491 (remove JUMP_IF_NOT_EG_MATCH, add CHECK_EG_MATCH, # add JUMP_BACKWARD_NO_INTERRUPT, make JUMP_NO_INTERRUPT virtual) +# Python 3.11a7 3494 (New location info table) # Python 3.12 will start with magic number 3500 diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 872f7283fc504e..69bea0ac550d10 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -234,6 +234,7 @@ def func(): pass co.co_endlinetable, co.co_columntable, co.co_exceptiontable, + co.co_locationtable, co.co_freevars, co.co_cellvars) @@ -315,6 +316,7 @@ def func(): co.co_endlinetable, co.co_columntable, co.co_exceptiontable, + co.co_locationtable, co.co_freevars, co.co_cellvars, ) @@ -333,10 +335,10 @@ def func(arg): newcode = code.replace(co_name="func") # Should not raise SystemError self.assertEqual(code, newcode) - def test_empty_linetable(self): + def test_empty_locationtable(self): def func(): pass - new_code = code = func.__code__.replace(co_linetable=b'') + new_code = code = func.__code__.replace(co_locationtable=b'') self.assertEqual(list(new_code.co_lines()), []) @requires_debug_ranges() @@ -383,9 +385,9 @@ def test_co_positions_artificial_instructions(self): ("LOAD_CONST", None), # artificial 'None' ("STORE_NAME", "e"), # XX: we know the location for this ("DELETE_NAME", "e"), - ("RERAISE", 1), - ("COPY", 3), - ("POP_EXCEPT", None), + ('RERAISE', 1), + ('COPY', 3), + ('POP_EXCEPT', None), ("RERAISE", 1) ] ) @@ -416,40 +418,15 @@ def f(): # co_positions behavior when info is missing. @requires_debug_ranges() - def test_co_positions_empty_linetable(self): + def test_co_positions_empty_locationtable(self): def func(): x = 1 - new_code = func.__code__.replace(co_linetable=b'') + new_code = func.__code__.replace(co_locationtable=b'') positions = new_code.co_positions() - next(positions) # Skip RESUME at start for line, end_line, column, end_column in positions: self.assertIsNone(line) self.assertEqual(end_line, new_code.co_firstlineno + 1) - @requires_debug_ranges() - def test_co_positions_empty_endlinetable(self): - def func(): - x = 1 - new_code = func.__code__.replace(co_endlinetable=b'') - positions = new_code.co_positions() - next(positions) # Skip RESUME at start - for line, end_line, column, end_column in positions: - self.assertEqual(line, new_code.co_firstlineno + 1) - self.assertIsNone(end_line) - - @requires_debug_ranges() - def test_co_positions_empty_columntable(self): - def func(): - x = 1 - new_code = func.__code__.replace(co_columntable=b'') - positions = new_code.co_positions() - next(positions) # Skip RESUME at start - for line, end_line, column, end_column in positions: - self.assertEqual(line, new_code.co_firstlineno + 1) - self.assertEqual(end_line, new_code.co_firstlineno + 1) - self.assertIsNone(column) - self.assertIsNone(end_column) - def isinterned(s): return s is sys.intern(('_' + s + '_')[1:-1]) @@ -527,6 +504,122 @@ def callback(code): self.assertFalse(bool(coderef())) self.assertTrue(self.called) +# Python implementation of location table parsing algorithm +def read(it): + return next(it) + +def read_varint(it): + b = read(it) + val = b & 63; + shift = 0; + while b & 64: + b = read(it) + shift += 6 + val |= (b&63) << shift + return val + +def read_signed_varint(it): + uval = read_varint(it) + if uval & 1: + return -(uval >> 1) + else: + return uval >> 1 + +def parse_location_table(code): + line = code.co_firstlineno + it = iter(code.co_locationtable) + while True: + try: + first_byte = read(it) + except StopIteration: + return + code = (first_byte >> 3) & 15 + length = (first_byte & 7) + 1 + if code == 15: + yield (code, length, None, None, None, None) + elif code == 14: + line_delta = read_signed_varint(it) + line += line_delta + end_line = line + read_varint(it) + col = read_varint(it) + if col == 0: + col = None + else: + col -= 1 + end_col = read_varint(it) + if end_col == 0: + end_col = None + else: + end_col -= 1 + yield (code, length, line, end_line, col, end_col) + elif code == 13: # No column + line_delta = read_signed_varint(it) + line += line_delta + yield (code, length, line, line, None, None) + elif code in (10, 11, 12): # new line + line_delta = code - 10 + line += line_delta + column = read(it) + end_column = read(it) + yield (code, length, line, line, column, end_column) + else: + assert (0 <= code < 10) + second_byte = read(it) + column = code << 3 | (second_byte >> 4) + yield (code, length, line, line, column, column + (second_byte & 15)) + +def positions_from_location_table(code): + for _, length, line, end_line, col, end_col in parse_location_table(code): + for _ in range(length): + yield (line, end_line, col, end_col) + +def misshappen(): + """ + + + + + + """ + x = ( + + + 4 + + + + + y + + ) + y = ( + a + + + b + + + + d + ) + return q if ( + + x + + ) else p + + +class CodeLocationTest(unittest.TestCase): + + def check_positions(self, func): + pos1 = list(func.__code__.co_positions()) + pos2 = list(positions_from_location_table(func.__code__)) + for l1, l2 in zip(pos1, pos2): + self.assertEqual(l1, l2) + self.assertEqual(len(pos1), len(pos2)) + + + def test_positions(self): + self.check_positions(parse_location_table) + self.check_positions(misshappen) + if check_impl_detail(cpython=True) and ctypes is not None: py = ctypes.pythonapi diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index a4e80805d3e5c8..5a9c618786f4e2 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -158,7 +158,9 @@ def test_leading_newlines(self): s256 = "".join(["\n"] * 256 + ["spam"]) co = compile(s256, 'fn', 'exec') self.assertEqual(co.co_firstlineno, 1) - self.assertEqual(list(co.co_lines()), [(0, 2, None), (2, 10, 257)]) + lines = list(co.co_lines()) + self.assertEqual(lines[0][2], None) + self.assertEqual(lines[1][2], 257) def test_literals_with_leading_zeroes(self): for arg in ["077787", "0xj", "0x.", "0e", "090000000000000", @@ -892,12 +894,19 @@ def no_code2(): with self.subTest(func=func): code = func.__code__ lines = list(code.co_lines()) - self.assertEqual(len(lines), 1) start, end, line = lines[0] self.assertEqual(start, 0) - self.assertEqual(end, len(code.co_code)) self.assertEqual(line, code.co_firstlineno) + def get_code_lines(self, code): + last_line = -2 + res = [] + for _, _, line in code.co_lines(): + if line is not None and line != last_line: + res.append(line - code.co_firstlineno) + last_line = line + return res + def test_lineno_attribute(self): def load_attr(): return ( @@ -939,9 +948,7 @@ def aug_store_attr(): for func, lines in zip(funcs, func_lines, strict=True): with self.subTest(func=func): - code_lines = [ line-func.__code__.co_firstlineno - for (_, _, line) in func.__code__.co_lines() - if line is not None ] + code_lines = self.get_code_lines(func.__code__) self.assertEqual(lines, code_lines) def test_line_number_genexp(self): @@ -952,11 +959,10 @@ def return_genexp(): x in y) - genexp_lines = [1, 3, 1] + genexp_lines = [0, 2, 0] genexp_code = return_genexp.__code__.co_consts[1] - code_lines = [ None if line is None else line-return_genexp.__code__.co_firstlineno - for (_, _, line) in genexp_code.co_lines() ] + code_lines = self.get_code_lines(genexp_code) self.assertEqual(genexp_lines, code_lines) def test_line_number_implicit_return_after_async_for(self): @@ -966,8 +972,7 @@ async def test(aseq): body expected_lines = [0, 1, 2, 1] - code_lines = [ None if line is None else line-test.__code__.co_firstlineno - for (_, _, line) in test.__code__.co_lines() ] + code_lines = self.get_code_lines(test.__code__) self.assertEqual(expected_lines, code_lines) def test_big_dict_literal(self): @@ -1112,14 +1117,14 @@ def test_multiline_expression(self): line=1, end_line=3, column=0, end_column=1) def test_very_long_line_end_offset(self): - # Make sure we get None for when the column offset is too large to - # store in a byte. + # Make sure we get the correct column offset for offsets + # too large to store in a byte. long_string = "a" * 1000 snippet = f"g('{long_string}')" compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'CALL', - line=1, end_line=1, column=None, end_column=None) + line=1, end_line=1, column=0, end_column=1005) def test_complex_single_line_expression(self): snippet = "a - b @ (c * x['key'] + 23)" diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 2f78d42cc724a6..09f046c0821c37 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -196,7 +196,7 @@ def bug42562(): # Set line number for 'pass' to None -bug42562.__code__ = bug42562.__code__.replace(co_linetable=b'\x04\x80') +bug42562.__code__ = bug42562.__code__.replace(co_locationtable=b'\xf8') dis_bug42562 = """\ @@ -1425,32 +1425,19 @@ def test_co_positions(self): @requires_debug_ranges() def test_co_positions_missing_info(self): code = compile('x, y, z', '', 'exec') - code_without_column_table = code.replace(co_columntable=b'') - actual = dis.get_instructions(code_without_column_table) + code_without_location_table = code.replace(co_locationtable=b'') + actual = dis.get_instructions(code_without_location_table) for instruction in actual: with self.subTest(instruction=instruction): positions = instruction.positions self.assertEqual(len(positions), 4) if instruction.opname == "RESUME": continue - self.assertEqual(positions.lineno, 1) - self.assertEqual(positions.end_lineno, 1) + self.assertIsNone(positions.lineno) + self.assertIsNone(positions.end_lineno) self.assertIsNone(positions.col_offset) self.assertIsNone(positions.end_col_offset) - code_without_endline_table = code.replace(co_endlinetable=b'') - actual = dis.get_instructions(code_without_endline_table) - for instruction in actual: - with self.subTest(instruction=instruction): - positions = instruction.positions - self.assertEqual(len(positions), 4) - if instruction.opname == "RESUME": - continue - self.assertEqual(positions.lineno, 1) - self.assertIsNone(positions.end_lineno) - self.assertIsNotNone(positions.col_offset) - self.assertIsNotNone(positions.end_col_offset) - # get_instructions has its own tests above, so can rely on it to validate # the object oriented API class BytecodeTests(InstructionTestCase, DisTestBase): diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 6dca79efef1802..55a1f419e01836 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2652,7 +2652,7 @@ def test_missing_lineno_shows_as_none(self): def f(): 1/0 self.lineno_after_raise(f, 1) - f.__code__ = f.__code__.replace(co_linetable=b'\x04\x80\xff\x80') + f.__code__ = f.__code__.replace(co_locationtable=b'\xf8\xf8\xf8\xf9\xf8\xf8\xf8') self.lineno_after_raise(f, None) def test_lineno_after_raise_in_with_exit(self): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 75d668df64d4c8..0f1ca2bc5b8478 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -615,6 +615,7 @@ def test_traceback_very_long_line(self): ' ^^^^^^^^^^\n' f' File "{TESTFN}", line {lineno_f}, in \n' f' {source}\n' + f' {"^"*len(source)}\n' ) self.assertEqual(result_lines, expected_error.splitlines()) diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index 272bcd6ea17b2a..43db2aa98c5732 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -6,7 +6,7 @@ PyDoc_STRVAR(code_new__doc__, "code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n" " flags, codestring, constants, names, varnames, filename, name,\n" " qualname, firstlineno, linetable, endlinetable, columntable,\n" -" exceptiontable, freevars=(), cellvars=(), /)\n" +" locationtable, exceptiontable, freevars=(), cellvars=(), /)\n" "--\n" "\n" "Create a code object. Not for the faint of heart."); @@ -18,8 +18,8 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, PyObject *columntable, - PyObject *exceptiontable, PyObject *freevars, - PyObject *cellvars); + PyObject *locationtable, PyObject *exceptiontable, + PyObject *freevars, PyObject *cellvars); static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -42,6 +42,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *linetable; PyObject *endlinetable; PyObject *columntable; + PyObject *locationtable; PyObject *exceptiontable; PyObject *freevars = NULL; PyObject *cellvars = NULL; @@ -51,7 +52,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) !_PyArg_NoKeywords("code", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 18, 20)) { + if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 19, 21)) { goto exit; } argcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); @@ -133,29 +134,30 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) linetable = PyTuple_GET_ITEM(args, 14); endlinetable = PyTuple_GET_ITEM(args, 15); columntable = PyTuple_GET_ITEM(args, 16); - if (!PyBytes_Check(PyTuple_GET_ITEM(args, 17))) { - _PyArg_BadArgument("code", "argument 18", "bytes", PyTuple_GET_ITEM(args, 17)); + locationtable = PyTuple_GET_ITEM(args, 17); + if (!PyBytes_Check(PyTuple_GET_ITEM(args, 18))) { + _PyArg_BadArgument("code", "argument 19", "bytes", PyTuple_GET_ITEM(args, 18)); goto exit; } - exceptiontable = PyTuple_GET_ITEM(args, 17); - if (PyTuple_GET_SIZE(args) < 19) { + exceptiontable = PyTuple_GET_ITEM(args, 18); + if (PyTuple_GET_SIZE(args) < 20) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 18))) { - _PyArg_BadArgument("code", "argument 19", "tuple", PyTuple_GET_ITEM(args, 18)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 19))) { + _PyArg_BadArgument("code", "argument 20", "tuple", PyTuple_GET_ITEM(args, 19)); goto exit; } - freevars = PyTuple_GET_ITEM(args, 18); - if (PyTuple_GET_SIZE(args) < 20) { + freevars = PyTuple_GET_ITEM(args, 19); + if (PyTuple_GET_SIZE(args) < 21) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 19))) { - _PyArg_BadArgument("code", "argument 20", "tuple", PyTuple_GET_ITEM(args, 19)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 20))) { + _PyArg_BadArgument("code", "argument 21", "tuple", PyTuple_GET_ITEM(args, 20)); goto exit; } - cellvars = PyTuple_GET_ITEM(args, 19); + cellvars = PyTuple_GET_ITEM(args, 20); skip_optional: - return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, endlinetable, columntable, exceptiontable, freevars, cellvars); + return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, endlinetable, columntable, locationtable, exceptiontable, freevars, cellvars); exit: return return_value; @@ -168,7 +170,8 @@ PyDoc_STRVAR(code_replace__doc__, " co_names=None, co_varnames=None, co_freevars=None,\n" " co_cellvars=None, co_filename=None, co_name=None,\n" " co_qualname=None, co_linetable=None, co_endlinetable=None,\n" -" co_columntable=None, co_exceptiontable=None)\n" +" co_columntable=None, co_locationtable=None,\n" +" co_exceptiontable=None)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -186,15 +189,16 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_columntable, PyBytesObject *co_exceptiontable); + PyObject *co_columntable, PyObject *co_locationtable, + PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_endlinetable", "co_columntable", "co_exceptiontable", NULL}; + static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_endlinetable", "co_columntable", "co_locationtable", "co_exceptiontable", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "replace", 0}; - PyObject *argsbuf[20]; + PyObject *argsbuf[21]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int co_argcount = self->co_argcount; int co_posonlyargcount = self->co_posonlyargcount; @@ -215,6 +219,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; PyObject *co_endlinetable = self->co_endlinetable; PyObject *co_columntable = self->co_columntable; + PyObject *co_locationtable = self->co_locationtable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); @@ -408,13 +413,19 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje goto skip_optional_kwonly; } } - if (!PyBytes_Check(args[19])) { - _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[19]); + if (args[19]) { + co_locationtable = args[19]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (!PyBytes_Check(args[20])) { + _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[20]); goto exit; } - co_exceptiontable = (PyBytesObject *)args[19]; + co_exceptiontable = (PyBytesObject *)args[20]; skip_optional_kwonly: - return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_endlinetable, co_columntable, co_exceptiontable); + return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_endlinetable, co_columntable, co_locationtable, co_exceptiontable); exit: return return_value; @@ -456,4 +467,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=b1b83a70ffc5b7cd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f4f0290f1ff3c264 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e872b398e08c8d..03aa6e53e2d2d3 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -311,6 +311,8 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_endlinetable = con->endlinetable; Py_INCREF(con->columntable); co->co_columntable = con->columntable; + Py_INCREF(con->locationtable); + co->co_locationtable = con->locationtable; Py_INCREF(con->consts); co->co_consts = con->consts; @@ -404,7 +406,8 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, - PyObject *columntable, PyObject *exceptiontable) + PyObject *columntable, PyObject *locationtable, + PyObject *exceptiontable) { PyCodeObject *co = NULL; PyObject *localsplusnames = NULL; @@ -484,6 +487,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, .linetable = linetable, .endlinetable = endlinetable, .columntable = columntable, + .locationtable = locationtable, .consts = consts, .names = names, @@ -529,13 +533,15 @@ PyCode_New(int argcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, - PyObject *columntable, PyObject *exceptiontable) + PyObject *columntable, PyObject *locationtable, + PyObject *exceptiontable) { return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable, - endlinetable, columntable, exceptiontable); + endlinetable, columntable, locationtable, + exceptiontable); } PyCodeObject * @@ -569,6 +575,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .linetable = emptystring, .endlinetable = emptystring, .columntable = emptystring, + .locationtable = emptystring, .consts = nulltuple, .names = nulltuple, .localsplusnames = nulltuple, @@ -605,148 +612,261 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) return _PyCode_CheckLineNumber(addrq, &bounds); } +void +_PyLocationTable_InitAddressRange(const char *locationtable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) +{ + range->opaque.lo_next = (const uint8_t *)locationtable; + range->opaque.limit = range->opaque.lo_next + length; + range->ar_start = -1; + range->ar_end = 0; + range->opaque.computed_line = firstlineno; + range->ar_line = -1; +} + int -PyCode_Addr2Location(PyCodeObject *co, int addrq, - int *start_line, int *start_column, - int *end_line, int *end_column) +_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) { - *start_line = PyCode_Addr2Line(co, addrq); - *start_column = _PyCode_Addr2Offset(co, addrq); - *end_line = _PyCode_Addr2EndLine(co, addrq); - *end_column = _PyCode_Addr2EndOffset(co, addrq); - return 1; + assert(co->co_locationtable != NULL); + const char *locationtable = PyBytes_AS_STRING(co->co_locationtable); + Py_ssize_t length = PyBytes_GET_SIZE(co->co_locationtable); + _PyLocationTable_InitAddressRange(locationtable, length, co->co_firstlineno, bounds); + return bounds->ar_line; } +/* Update *bounds to describe the first and one-past-the-last instructions in + the same line as lasti. Return the number of that line, or -1 if lasti is out of bounds. */ int -_PyCode_Addr2EndLine(PyCodeObject* co, int addrq) +_PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds) { - if (addrq < 0) { - return co->co_firstlineno; + while (bounds->ar_end <= lasti) { + if (!_PyLineTable_NextAddressRange(bounds)) { + return -1; + } } - else if (co->co_endlinetable == Py_None) { - return -1; + while (bounds->ar_start > lasti) { + if (!_PyLineTable_PreviousAddressRange(bounds)) { + return -1; + } } + return bounds->ar_line; +} - assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); - PyCodeAddressRange bounds; - _PyCode_InitEndAddressRange(co, &bounds); - return _PyCode_CheckLineNumber(addrq, &bounds); +static int +scan_varint(const uint8_t *ptr) +{ + int read = *ptr++; + int val = read & 63; + int shift = 0; + while (read & 64) { + read = *ptr++; + shift += 6; + val |= (read & 63) << shift; + } + return val; } -int -_PyCode_Addr2Offset(PyCodeObject* co, int addrq) +static int +scan_signed_varint(const uint8_t *ptr) { - if (co->co_columntable == Py_None || addrq < 0) { - return -1; + int uval = scan_varint(ptr); + if (uval & 1) { + return -(int)(uval >> 1); } - addrq /= sizeof(_Py_CODEUNIT); - if (addrq*2 >= PyBytes_GET_SIZE(co->co_columntable)) { - return -1; + else { + return uval >> 1; } - - unsigned char* bytes = (unsigned char*)PyBytes_AS_STRING(co->co_columntable); - return bytes[addrq*2] - 1; } -int -_PyCode_Addr2EndOffset(PyCodeObject* co, int addrq) +static int +get_next_line_delta(PyCodeAddressRange *bounds) { - if (co->co_columntable == Py_None || addrq < 0) { - return -1; - } - addrq /= sizeof(_Py_CODEUNIT); - if (addrq*2+1 >= PyBytes_GET_SIZE(co->co_columntable)) { - return -1; + int code = ((*bounds->opaque.lo_next) >> 3) & 15; + switch (code) { + case 15: + return 0; + case 13: /* No column */ + case 14: /* Long form */ + return scan_signed_varint(bounds->opaque.lo_next+1); + case 10: + case 11: + case 12: + /* One line forms */ + return code - 10; + default: + /* Same line */ + return 0; } +} - unsigned char* bytes = (unsigned char*)PyBytes_AS_STRING(co->co_columntable); - return bytes[addrq*2+1] - 1; +static int +is_no_line_marker(uint8_t b) +{ + return (b >> 3) == 31; } -void -_PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) + +#define ASSERT_VALID_BOUNDS(bounds) \ + assert(bounds->opaque.lo_next <= bounds->opaque.limit && \ + (bounds->ar_line == -1 || bounds->ar_line == bounds->opaque.computed_line) && \ + (bounds->opaque.lo_next == bounds->opaque.limit || \ + (*bounds->opaque.lo_next) & 128)) + +static int +next_code_delta(PyCodeAddressRange *bounds) { - range->opaque.lo_next = linetable; - range->opaque.limit = range->opaque.lo_next + length; - range->ar_start = -1; - range->ar_end = 0; - range->opaque.computed_line = firstlineno; - range->ar_line = -1; + assert((*bounds->opaque.lo_next) & 128); + return (((*bounds->opaque.lo_next) & 7) + 1) * sizeof(_Py_CODEUNIT); } -int -_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) +static int +read_byte(PyCodeAddressRange *bounds) { - const char *linetable = PyBytes_AS_STRING(co->co_linetable); - Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); - _PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); - return bounds->ar_line; + return *bounds->opaque.lo_next++; } -int -_PyCode_InitEndAddressRange(PyCodeObject* co, PyCodeAddressRange* bounds) +static int +read_varint(PyCodeAddressRange *bounds) { - char* linetable = PyBytes_AS_STRING(co->co_endlinetable); - Py_ssize_t length = PyBytes_GET_SIZE(co->co_endlinetable); - _PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); - return bounds->ar_line; + int read = read_byte(bounds); + int val = read & 63; + int shift = 0; + while (read & 64) { + read = read_byte(bounds); + shift += 6; + val |= (read & 63) << shift; + } + return val; } -/* Update *bounds to describe the first and one-past-the-last instructions in - the same line as lasti. Return the number of that line, or -1 if lasti is out of bounds. */ -int -_PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds) +static int +read_signed_varint(PyCodeAddressRange *bounds) { - while (bounds->ar_end <= lasti) { - if (!_PyLineTable_NextAddressRange(bounds)) { - return -1; - } + int uval = read_varint(bounds); + if (uval & 1) { + return -(int)(uval >> 1); } - while (bounds->ar_start > lasti) { - if (!_PyLineTable_PreviousAddressRange(bounds)) { - return -1; - } + else { + return uval >> 1; } - return bounds->ar_line; } static void retreat(PyCodeAddressRange *bounds) { - int ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; - if (ldelta == -128) { - ldelta = 0; - } - bounds->opaque.computed_line -= ldelta; - bounds->opaque.lo_next -= 2; + ASSERT_VALID_BOUNDS(bounds); + assert(bounds->ar_start > 0); + do { + bounds->opaque.lo_next--; + } while (((*bounds->opaque.lo_next) & 128) == 0); + bounds->opaque.computed_line -= get_next_line_delta(bounds); bounds->ar_end = bounds->ar_start; - bounds->ar_start -= ((unsigned char *)bounds->opaque.lo_next)[-2]; - ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; - if (ldelta == -128) { + bounds->ar_start -= next_code_delta(bounds); + if (is_no_line_marker(bounds->opaque.lo_next[-1])) { bounds->ar_line = -1; } else { bounds->ar_line = bounds->opaque.computed_line; } + ASSERT_VALID_BOUNDS(bounds); } static void advance(PyCodeAddressRange *bounds) { - bounds->ar_start = bounds->ar_end; - int delta = ((unsigned char *)bounds->opaque.lo_next)[0]; - bounds->ar_end += delta; - int ldelta = ((signed char *)bounds->opaque.lo_next)[1]; - bounds->opaque.lo_next += 2; - if (ldelta == -128) { + ASSERT_VALID_BOUNDS(bounds); + bounds->opaque.computed_line += get_next_line_delta(bounds); + if (is_no_line_marker(*bounds->opaque.lo_next)) { bounds->ar_line = -1; } else { - bounds->opaque.computed_line += ldelta; bounds->ar_line = bounds->opaque.computed_line; } + bounds->ar_start = bounds->ar_end; + bounds->ar_end += next_code_delta(bounds); + do { + bounds->opaque.lo_next++; + } while (bounds->opaque.lo_next < bounds->opaque.limit && + ((*bounds->opaque.lo_next) & 128) == 0); + ASSERT_VALID_BOUNDS(bounds); +} + +static void +advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, int *endcolumn) +{ + ASSERT_VALID_BOUNDS(bounds); + int first_byte = read_byte(bounds); + int code = (first_byte >> 3) & 15; + bounds->ar_start = bounds->ar_end; + bounds->ar_end = bounds->ar_start + ((first_byte & 7) + 1) * sizeof(_Py_CODEUNIT); + switch(code) { + case 15: + /* None */ + bounds->ar_line = *endline = -1; + *column = *endcolumn = -1; + break; + case 14: + { + /* Long form */ + bounds->opaque.computed_line += read_signed_varint(bounds); + bounds->ar_line = bounds->opaque.computed_line; + *endline = bounds->ar_line + read_varint(bounds); + *column = read_varint(bounds)-1; + *endcolumn = read_varint(bounds)-1; + break; + } + case 13: + { + /* No column */ + bounds->opaque.computed_line += read_signed_varint(bounds); + *endline = bounds->ar_line = bounds->opaque.computed_line; + *column = *endcolumn = -1; + break; + } + case 10: + case 11: + case 12: + { + /* one line form */ + int line_delta = code - 10; + bounds->opaque.computed_line += line_delta; + *endline = bounds->ar_line = bounds->opaque.computed_line; + *column = read_byte(bounds); + *endcolumn = read_byte(bounds); + break; + } + default: /* 0 to 9 */ + { + /* Short form */ + int second_byte = read_byte(bounds); + assert((second_byte & 128) == 0); + *endline = bounds->ar_line = bounds->opaque.computed_line; + *column = code << 3 | (second_byte >> 4); + *endcolumn = *column + (second_byte & 15); + } + } + ASSERT_VALID_BOUNDS(bounds); } +int +PyCode_Addr2Location(PyCodeObject *co, int addrq, + int *start_line, int *start_column, + int *end_line, int *end_column) +{ + if (addrq < 0) { + *start_line = *end_line = co->co_firstlineno; + *start_column = *end_column = 0; + } + assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); + PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + _PyCode_CheckLineNumber(addrq, &bounds); + retreat(&bounds); + advance_with_locations(&bounds, end_line, start_column, end_column); + *start_line = bounds.ar_line; + return 1; +} + + static inline int at_end(PyCodeAddressRange *bounds) { return bounds->opaque.lo_next >= bounds->opaque.limit; @@ -759,10 +879,7 @@ _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range) return 0; } retreat(range); - while (range->ar_start == range->ar_end) { - assert(range->ar_start > 0); - retreat(range); - } + assert(range->ar_end > range->ar_start); return 1; } @@ -773,13 +890,37 @@ _PyLineTable_NextAddressRange(PyCodeAddressRange *range) return 0; } advance(range); - while (range->ar_start == range->ar_end) { - assert(!at_end(range)); - advance(range); - } + assert(range->ar_end > range->ar_start); return 1; } +int +_PyLineTable_StartsLine(PyCodeAddressRange *range) +{ + if (range->ar_start <= 0) { + return 0; + } + const uint8_t *ptr = range->opaque.lo_next; + do { + ptr--; + } while (((*ptr) & 128) == 0); + int code = ((*ptr)>> 3) & 15; + switch(code) { + case 15: + return 0; + case 13: + case 14: + return ptr[1] != 0; + case 10: + return 0; + case 11: + case 12: + return 1; + default: + return 0; + } +} + static int emit_pair(PyObject **bytes, int *offset, int a, int b) { @@ -856,7 +997,6 @@ typedef struct { PyObject_HEAD PyCodeObject *li_code; PyCodeAddressRange li_line; - char *li_end; } lineiterator; @@ -962,7 +1102,11 @@ new_linesiterator(PyCodeObject *code) typedef struct { PyObject_HEAD PyCodeObject* pi_code; + PyCodeAddressRange pi_range; int pi_offset; + int pi_endline; + int pi_column; + int pi_endcolumn; } positionsiterator; static void @@ -983,22 +1127,19 @@ _source_offset_converter(int* value) { static PyObject* positionsiter_next(positionsiterator* pi) { - if (pi->pi_offset >= _PyCode_NBYTES(pi->pi_code)) { - return NULL; - } - - int start_line, start_col, end_line, end_col; - if (!PyCode_Addr2Location(pi->pi_code, pi->pi_offset, &start_line, - &start_col, &end_line, &end_col)) { - return NULL; + if (pi->pi_offset >= pi->pi_range.ar_end) { + assert(pi->pi_offset == pi->pi_range.ar_end); + if (at_end(&pi->pi_range)) { + return NULL; + } + advance_with_locations(&pi->pi_range, &pi->pi_endline, &pi->pi_column, &pi->pi_endcolumn); } - pi->pi_offset += 2; return Py_BuildValue("(O&O&O&O&)", - _source_offset_converter, &start_line, - _source_offset_converter, &end_line, - _source_offset_converter, &start_col, - _source_offset_converter, &end_col); + _source_offset_converter, &pi->pi_range.ar_line, + _source_offset_converter, &pi->pi_endline, + _source_offset_converter, &pi->pi_column, + _source_offset_converter, &pi->pi_endcolumn); } static PyTypeObject PositionsIterator = { @@ -1053,7 +1194,8 @@ code_positionsiterator(PyCodeObject* code, PyObject* Py_UNUSED(args)) } Py_INCREF(code); pi->pi_code = code; - pi->pi_offset = 0; + _PyCode_InitAddressRange(code, &pi->pi_range); + pi->pi_offset = pi->pi_range.ar_end; return (PyObject*)pi; } @@ -1205,6 +1347,7 @@ code.__new__ as code_new linetable: object(subclass_of="&PyBytes_Type") endlinetable: object columntable: object + locationtable: object exceptiontable: object(subclass_of="&PyBytes_Type") freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () @@ -1220,9 +1363,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, PyObject *columntable, - PyObject *exceptiontable, PyObject *freevars, - PyObject *cellvars) -/*[clinic end generated code: output=e1d2086aa8da7c08 input=a06cd92369134063]*/ + PyObject *locationtable, PyObject *exceptiontable, + PyObject *freevars, PyObject *cellvars) +/*[clinic end generated code: output=25d03c9913c257fd input=e4d7c37197830e7f]*/ { PyObject *co = NULL; PyObject *ournames = NULL; @@ -1301,7 +1444,8 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, ourcellvars, filename, name, qualname, firstlineno, linetable, endlinetable, - columntable, exceptiontable + columntable, locationtable, + exceptiontable ); cleanup: Py_XDECREF(ournames); @@ -1339,6 +1483,7 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_endlinetable); Py_XDECREF(co->co_columntable); + Py_XDECREF(co->co_locationtable); Py_XDECREF(co->co_exceptiontable); if (co->co_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject*)co); @@ -1490,6 +1635,7 @@ static PyMemberDef code_memberlist[] = { {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, {"co_endlinetable", T_OBJECT, OFF(co_endlinetable), READONLY}, {"co_columntable", T_OBJECT, OFF(co_columntable), READONLY}, + {"co_locationtable", T_OBJECT, OFF(co_locationtable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ }; @@ -1587,6 +1733,7 @@ code.replace co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None co_endlinetable: object(c_default="self->co_endlinetable") = None co_columntable: object(c_default="self->co_columntable") = None + co_locationtable: object(c_default="self->co_locationtable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None Return a copy of the code object with new values for the specified fields. @@ -1602,8 +1749,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_columntable, PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=f046bf0be3bab91f input=78dbe204dbd06c2f]*/ + PyObject *co_columntable, PyObject *co_locationtable, + PyBytesObject *co_exceptiontable) +/*[clinic end generated code: output=4d1492921dd31aef input=3e19c37f55c87e86]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1681,7 +1829,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_firstlineno, (PyObject*)co_linetable, (PyObject*)co_endlinetable, (PyObject*)co_columntable, - (PyObject*)co_exceptiontable); + (PyObject*)co_locationtable, (PyObject*)co_exceptiontable); error: Py_XDECREF(code); diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index a3c09529116cc1..e9895b53f83d6a 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -42,5 +42,15 @@ unsigned char M_test_frozenmain[] = { 5,8,5,10,5,10,11,41,21,24,11,41,11,41,28,34, 35,38,28,39,28,39,28,39,28,39,28,39,11,41,11,41, 5,42,5,42,5,42,5,42,5,42,5,42,5,42,5,42, - 5,42,1,42,1,42,114,9,0,0,0, + 5,42,1,42,1,42,243,152,0,0,0,248,240,6,0,1, + 11,128,10,128,10,128,10,216,0,24,208,0,24,208,0,24, + 208,0,24,224,0,5,128,5,208,6,26,209,0,27,212,0, + 27,208,0,27,216,0,5,128,5,128,106,144,35,148,40,209, + 0,27,212,0,27,208,0,27,216,9,38,208,9,26,212,9, + 38,209,9,40,212,9,40,168,24,212,9,50,128,6,240,2, + 6,12,2,240,0,7,1,42,240,0,7,1,42,128,67,240, + 14,0,5,10,128,69,208,10,40,144,67,208,10,40,208,10, + 40,152,54,160,35,156,59,208,10,40,208,10,40,209,4,41, + 212,4,41,208,4,41,208,4,41,240,15,7,1,42,240,0, + 7,1,42,114,9,0,0,0, }; diff --git a/Python/compile.c b/Python/compile.c index f04ba9ec50f6fd..4db141ec6df7d9 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7012,6 +7012,7 @@ compiler_match(struct compiler *c, stmt_ty s) XXX must handle implicit jumps from one block to next */ + struct assembler { PyObject *a_bytecode; /* bytes containing bytecode */ int a_offset; /* offset into bytecode */ @@ -7030,6 +7031,11 @@ struct assembler { int a_end_lineno; /* end_lineno of last emitted instruction */ int a_lineno_start; /* bytecode start offset of current lineno */ int a_end_lineno_start; /* bytecode start offset of current end_lineno */ + + /* Location Info */ + PyObject* a_locationtable; /* bytes containing location info */ + int a_location_off; /* offset of last written location info frame */ + basicblock *a_entry; }; @@ -7131,6 +7137,8 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) a->a_lnotab = NULL; a->a_enotab = NULL; a->a_cnotab = NULL; + a->a_locationtable = NULL; + a->a_location_off = 0; a->a_cnotab_off = 0; a->a_except_table = NULL; a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); @@ -7149,6 +7157,10 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) if (a->a_cnotab == NULL) { goto error; } + a->a_locationtable = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); + if (a->a_lnotab == NULL) { + goto error; + } a->a_except_table = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); if (a->a_except_table == NULL) { goto error; @@ -7562,6 +7574,158 @@ assemble_cnotab(struct assembler* a, struct instr* i, int instr_size) return 1; } +/* Code location formats (this looks better if rendered as markdown) + +Format | First byte | Subsequent bytes | Meaning +------ | ------- | ------- | ------- +Short | 1nnnnbbb | 0ccceeee | Same line as previous entry. Column = `nnnn`*8 + `ccc`, end column = column + `eeee`. `n` < 10 +Indent | 11xxxbbb | 0ccceeee | One line. Line = previous + xxx-1. Column = `ccc`*4, end column = column + `eeee`. 2 <= x < 5 +Long form | 11110bbb | signed varint `l`, varint `e`, unsigned varint `c` ,unsigned varint `x` | Line = previous + `l`, end_line = line + `e`, column = c, end_column = x +None | 11111bbb | --- | No location info + +No column | 11101bbb | + +*/ + + +static void +write_location_byte(struct assembler* a, int val) +{ + PyBytes_AS_STRING(a->a_locationtable)[a->a_location_off] = val&255; + a->a_location_off++; +} + +static void +write_location_varint(struct assembler* a, unsigned int val) +{ + while (val >= 64) { + write_location_byte(a, 64 | (val & 63)); + val >>= 6; + } + write_location_byte(a, val); +} + +static void +write_location_signed_varint(struct assembler* a, int val) +{ + if (val < 0) { + val = ((-val)<<1) | 1; + } + else { + val = val << 1; + } + write_location_varint(a, val); +} + +static void +write_location_info_short_form(struct assembler* a, int length, int column, int end_column) +{ + assert(length > 0 && length <= 8); + int column_low_bits = column & 7; + int column_high_bits = column & 0x78; + assert(column < 80); + assert(end_column - column < 16); + write_location_byte(a, 0x80 | column_high_bits | (length - 1)); + write_location_byte(a, (column_low_bits << 4) | (end_column - column)); +} + +static void +write_location_info_oneline_form(struct assembler* a, int length, int line_delta, int column, int end_column) +{ + assert(length > 0 && length <= 8); + assert(line_delta >= 0 && line_delta < 3); + assert(column < 128); + assert(end_column < 128); + write_location_byte(a, 0x80 | (line_delta + 10) << 3 | (length - 1)); + write_location_byte(a, column); + write_location_byte(a, end_column); +} + +static void +write_location_info_long_form(struct assembler* a, struct instr* i, int length) +{ + assert(length > 0 && length <= 8); + write_location_byte(a, 0xf0 | (length - 1)); + write_location_signed_varint(a, i->i_lineno - a->a_lineno); + write_location_varint(a, i->i_end_lineno - i->i_lineno); + write_location_varint(a, i->i_col_offset+1); + write_location_varint(a, i->i_end_col_offset+1); +} + +static void +write_location_info_none(struct assembler* a, int length) +{ + write_location_byte(a, 0xf8 | (length-1)); +} + +static void +write_location_info_no_column(struct assembler* a, int length, int line_delta) +{ + write_location_byte(a, 0xe8 | (length-1)); + write_location_signed_varint(a, line_delta); +} + +#define THEORETICAL_MAX_ENTRY_SIZE 25 /* 1 + 6 + 6 + 6 + 6 */ + +static void +write_location_info_entry(struct assembler* a, struct instr* i, int isize) +{ + if (i->i_lineno < 0) { + write_location_info_none(a, isize); + return; + } + int line_delta = i->i_lineno - a->a_lineno; + int column = i->i_col_offset; + int end_column = i->i_end_col_offset; + assert(column >= -1); + assert(end_column >= -1); + if (column < 0 || end_column < 0) { + if (i->i_end_lineno == i->i_lineno) { + write_location_info_no_column(a, isize, line_delta); + a->a_lineno = i->i_lineno; + return; + } + } + else if (i->i_end_lineno == i->i_lineno) { + if (line_delta == 0 && column < 80 && end_column - column < 16) { + write_location_info_short_form(a, isize, column, end_column); + return; + } + if (line_delta >= 0 && line_delta < 3 && column < 128 && end_column < 128) { + write_location_info_oneline_form(a, isize, line_delta, column, end_column); + a->a_lineno = i->i_lineno; + return; + } + } + write_location_info_long_form(a, i, isize); + a->a_lineno = i->i_lineno; +} + +static int +assemble_emit_location(struct assembler* a, struct instr* i) +{ + Py_ssize_t len = PyBytes_GET_SIZE(a->a_locationtable); + if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { + assert(len > THEORETICAL_MAX_ENTRY_SIZE); + if (_PyBytes_Resize(&a->a_locationtable, len*2) < 0) { + return 0; + } + } + int isize = instr_size(i); + if (isize > 8) { + write_location_info_entry(a, i, 8); + isize -= 8; + while (isize > 8) { + write_location_info_none(a, 8); + isize -= 8; + } + write_location_info_none(a, isize); + } + else { + write_location_info_entry(a, i, isize); + } + return 1; +} /* assemble_emit() Extend the bytecode with a new instruction. @@ -7903,6 +8067,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist, .linetable = a->a_lnotab, .endlinetable = a->a_enotab, .columntable = a->a_cnotab, + .locationtable = a->a_locationtable, .consts = consts, .names = names, @@ -8337,6 +8502,14 @@ assemble(struct compiler *c, int addNone) goto error; } + /* Emit location info */ + a.a_lineno = c->u->u_firstlineno; + for(b = entryblock; b != NULL; b = b->b_next) { + for (j = 0; j < b->b_iused; j++) + if (!assemble_emit_location(&a, &b->b_instr[j])) + goto error; + } + if (!assemble_exception_table(&a)) { goto error; } @@ -8367,6 +8540,9 @@ assemble(struct compiler *c, int addNone) if (_PyBytes_Resize(&a.a_cnotab, a.a_cnotab_off) < 0) { goto error; } + if (_PyBytes_Resize(&a.a_locationtable, a.a_location_off) < 0) { + goto error; + } if (!merge_const_one(c, &a.a_cnotab)) { goto error; } diff --git a/Python/marshal.c b/Python/marshal.c index 19abcc8ffe4b76..1315cae525ed10 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -566,6 +566,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(co->co_linetable, p); w_object(co->co_endlinetable, p); w_object(co->co_columntable, p); + w_object(co->co_locationtable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); } @@ -1360,6 +1361,7 @@ r_object(RFILE *p) PyObject *linetable = NULL; PyObject* endlinetable = NULL; PyObject* columntable = NULL; + PyObject* locationtable = NULL; PyObject *exceptiontable = NULL; idx = r_ref_reserve(flag, p); @@ -1421,6 +1423,9 @@ r_object(RFILE *p) columntable = r_object(p); if (columntable == NULL) goto code_error; + locationtable = r_object(p); + if (locationtable == NULL) + goto code_error; exceptiontable = r_object(p); if (exceptiontable == NULL) goto code_error; @@ -1436,6 +1441,7 @@ r_object(RFILE *p) .linetable = linetable, .endlinetable = endlinetable, .columntable = columntable, + .locationtable = locationtable, .consts = consts, .names = names, diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index dfa4b3a8eeb011..2182d810abf53a 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -240,6 +240,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_linetable = self.generate(name + "_linetable", code.co_linetable) co_endlinetable = self.generate(name + "_endlinetable", code.co_endlinetable) co_columntable = self.generate(name + "_columntable", code.co_columntable) + co_locationtable = self.generate(name + "_locationtable", code.co_locationtable) co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable) # These fields are not directly accessible localsplusnames, localspluskinds = get_localsplus(code) @@ -280,6 +281,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f".co_linetable = {co_linetable},") self.write(f".co_endlinetable = {co_endlinetable},") self.write(f".co_columntable = {co_columntable},") + self.write(f".co_locationtable = {co_locationtable},") self.write(f".co_code_adaptive = {co_code_adaptive},") name_as_code = f"(PyCodeObject *)&{name}" self.deallocs.append(f"_PyStaticCode_Dealloc({name_as_code});") diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index 2eaaa7ce2d95bc..3ab3b000616ec7 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -291,6 +291,7 @@ def R_REF(obj: Any) -> Any: retval.co_linetable = self.r_object() retval.co_endlinetable = self.r_object() retval.co_columntable = self.r_object() + retval.co_locationtable = self.r_object() retval.co_exceptiontable = self.r_object() return retval elif type == Type.REF: From f603e3f851ce9e2c6fc3bf48a45bb6e06fe8745b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Apr 2022 16:10:00 +0100 Subject: [PATCH 02/18] Get compact debug table (almost) working. --- Lib/test/test_marshal.py | 8 +- Objects/codeobject.c | 188 +++++++++++++++++++++++++++------------ Objects/frameobject.c | 6 +- Python/compile.c | 2 + Python/marshal.c | 1 + 5 files changed, 145 insertions(+), 60 deletions(-) diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 8d55382b195a18..2f1462f0e85ccf 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -131,16 +131,16 @@ def test_different_filenames(self): @requires_debug_ranges() def test_no_columntable_and_endlinetable_with_no_debug_ranges(self): # Make sure when demarshalling objects with `-X no_debug_ranges` - # that the columntable and endlinetable are None. + # that the columns are None. co = ExceptionTestCase.test_exceptions.__code__ code = textwrap.dedent(""" import sys import marshal with open(sys.argv[1], 'rb') as f: co = marshal.load(f) - - assert co.co_endlinetable is None - assert co.co_columntable is None + positions = list(co.co_positions()) + assert positions[0][2] is None + assert positions[0][3] is None """) try: diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 03aa6e53e2d2d3..5e4fe412e7c5db 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -349,6 +349,118 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) PyBytes_GET_SIZE(con->code)); } +static int +scan_varint(const uint8_t *ptr) +{ + int read = *ptr++; + int val = read & 63; + int shift = 0; + while (read & 64) { + read = *ptr++; + shift += 6; + val |= (read & 63) << shift; + } + return val; +} + +static int +scan_signed_varint(const uint8_t *ptr) +{ + int uval = scan_varint(ptr); + if (uval & 1) { + return -(int)(uval >> 1); + } + else { + return uval >> 1; + } +} + +static int +get_line_delta(const uint8_t *ptr) +{ + int code = ((*ptr) >> 3) & 15; + switch (code) { + case 15: + return 0; + case 13: /* No column */ + case 14: /* Long form */ + return scan_signed_varint(ptr+1); + case 10: + case 11: + case 12: + /* One line forms */ + return code - 10; + default: + /* Same line */ + return 0; + } +} + +static uint8_t * +write_varint(uint8_t *ptr, unsigned int val) +{ + while (val >= 64) { + *ptr++ = 64 | (val & 63); + val >>= 6; + } + *ptr++ = val; + return ptr; +} + +static uint8_t * +write_signed_varint(uint8_t *ptr, int val) +{ + if (val < 0) { + val = ((-val)<<1) | 1; + } + else { + val = val << 1; + } + return write_varint(ptr, val); +} + +static PyObject * +remove_column_info(PyObject *locations) +{ + int offset = 0; + const uint8_t *data = (const uint8_t *)PyBytes_AS_STRING(locations); + PyObject *res = PyBytes_FromStringAndSize(NULL, 32); + if (res == NULL) { + PyErr_NoMemory(); + return NULL; + } + uint8_t *output = (uint8_t *)PyBytes_AS_STRING(res); + while (offset < PyBytes_GET_SIZE(locations)) { + Py_ssize_t len = PyBytes_GET_SIZE(res); + Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); + if (write_offset + 16 >= PyBytes_GET_SIZE(res)) { + if (_PyBytes_Resize(&res, len * 2) < 0) { + return NULL; + } + output = (uint8_t *)PyBytes_AS_STRING(res) + write_offset; + } + int blength = data[offset] & 7; + int code = (data[offset] >> 3) & 15; + if (code == 15) { + *output++ = 0xf8 | blength; + } + else { + int ldelta = get_line_delta(&data[offset]); + *output++ = 0xe8 | blength; + output = write_signed_varint(output, ldelta); + } + offset++; + while ((data[offset] & 128) == 0) { + offset++; + } + } + Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); + if (_PyBytes_Resize(&res, write_offset)) { + return NULL; + } + return res; +} + /* The caller is responsible for ensuring that the given data is valid. */ PyCodeObject * @@ -375,11 +487,15 @@ _PyCode_New(struct _PyCodeConstructor *con) return NULL; } + PyObject *replacement_locations = NULL; // Discard the endlinetable and columntable if we are opted out of debug // ranges. if (!_Py_GetConfig()->code_debug_ranges) { - con->endlinetable = Py_None; - con->columntable = Py_None; + replacement_locations = remove_column_info(con->locationtable); + if (replacement_locations == NULL) { + return NULL; + } + con->locationtable = replacement_locations; } Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); @@ -389,7 +505,7 @@ _PyCode_New(struct _PyCodeConstructor *con) return NULL; } init_code(co, con); - + Py_XDECREF(replacement_locations); return co; } @@ -651,57 +767,10 @@ _PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds) return bounds->ar_line; } -static int -scan_varint(const uint8_t *ptr) -{ - int read = *ptr++; - int val = read & 63; - int shift = 0; - while (read & 64) { - read = *ptr++; - shift += 6; - val |= (read & 63) << shift; - } - return val; -} - -static int -scan_signed_varint(const uint8_t *ptr) -{ - int uval = scan_varint(ptr); - if (uval & 1) { - return -(int)(uval >> 1); - } - else { - return uval >> 1; - } -} - -static int -get_next_line_delta(PyCodeAddressRange *bounds) -{ - int code = ((*bounds->opaque.lo_next) >> 3) & 15; - switch (code) { - case 15: - return 0; - case 13: /* No column */ - case 14: /* Long form */ - return scan_signed_varint(bounds->opaque.lo_next+1); - case 10: - case 11: - case 12: - /* One line forms */ - return code - 10; - default: - /* Same line */ - return 0; - } -} - static int is_no_line_marker(uint8_t b) { - return (b >> 3) == 31; + return (b >> 3) == 0x1f; } @@ -718,6 +787,16 @@ next_code_delta(PyCodeAddressRange *bounds) return (((*bounds->opaque.lo_next) & 7) + 1) * sizeof(_Py_CODEUNIT); } +static int +previous_code_delta(PyCodeAddressRange *bounds) +{ + const uint8_t *ptr = bounds->opaque.lo_next-1; + while (((*ptr) & 128) == 0) { + ptr--; + } + return (((*ptr) & 7) + 1) * sizeof(_Py_CODEUNIT); +} + static int read_byte(PyCodeAddressRange *bounds) { @@ -758,9 +837,9 @@ retreat(PyCodeAddressRange *bounds) do { bounds->opaque.lo_next--; } while (((*bounds->opaque.lo_next) & 128) == 0); - bounds->opaque.computed_line -= get_next_line_delta(bounds); + bounds->opaque.computed_line -= get_line_delta(bounds->opaque.lo_next); bounds->ar_end = bounds->ar_start; - bounds->ar_start -= next_code_delta(bounds); + bounds->ar_start -= previous_code_delta(bounds); if (is_no_line_marker(bounds->opaque.lo_next[-1])) { bounds->ar_line = -1; } @@ -774,7 +853,7 @@ static void advance(PyCodeAddressRange *bounds) { ASSERT_VALID_BOUNDS(bounds); - bounds->opaque.computed_line += get_next_line_delta(bounds); + bounds->opaque.computed_line += get_line_delta(bounds->opaque.lo_next); if (is_no_line_marker(*bounds->opaque.lo_next)) { bounds->ar_line = -1; } @@ -846,7 +925,6 @@ advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, in } ASSERT_VALID_BOUNDS(bounds); } - int PyCode_Addr2Location(PyCodeObject *co, int addrq, int *start_line, int *start_column, diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6842e62839fd14..7d427e5df70b96 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -367,6 +367,7 @@ marklines(PyCodeObject *code, int len) PyCodeAddressRange bounds; _PyCode_InitAddressRange(code, &bounds); assert (bounds.ar_end == 0); + int last_line = -1; int *linestarts = PyMem_New(int, len); if (linestarts == NULL) { @@ -378,7 +379,10 @@ marklines(PyCodeObject *code, int len) while (_PyLineTable_NextAddressRange(&bounds)) { assert(bounds.ar_start / (int)sizeof(_Py_CODEUNIT) < len); - linestarts[bounds.ar_start / sizeof(_Py_CODEUNIT)] = bounds.ar_line; + if (bounds.ar_line != last_line && bounds.ar_line != -1) { + linestarts[bounds.ar_start / sizeof(_Py_CODEUNIT)] = bounds.ar_line; + last_line = bounds.ar_line; + } } return linestarts; } diff --git a/Python/compile.c b/Python/compile.c index 4db141ec6df7d9..79e009d300c1ed 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7175,6 +7175,7 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) Py_XDECREF(a->a_lnotab); Py_XDECREF(a->a_enotab); Py_XDECREF(a->a_cnotab); + Py_XDECREF(a->a_locationtable); Py_XDECREF(a->a_except_table); return 0; } @@ -7186,6 +7187,7 @@ assemble_free(struct assembler *a) Py_XDECREF(a->a_lnotab); Py_XDECREF(a->a_enotab); Py_XDECREF(a->a_cnotab); + Py_XDECREF(a->a_locationtable); Py_XDECREF(a->a_except_table); } diff --git a/Python/marshal.c b/Python/marshal.c index 1315cae525ed10..cc29d5c0339bc8 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1481,6 +1481,7 @@ r_object(RFILE *p) Py_XDECREF(linetable); Py_XDECREF(endlinetable); Py_XDECREF(columntable); + Py_XDECREF(locationtable); Py_XDECREF(exceptiontable); } retval = v; From 34ec9d9fc3b3fadb4e750715fad8d10a69475de3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Apr 2022 16:58:57 +0100 Subject: [PATCH 03/18] Remove column table from code objects. --- Include/cpython/code.h | 6 +-- Include/internal/pycore_code.h | 1 - Lib/test/test_code.py | 25 +++++++------ Objects/clinic/codeobject.c.h | 68 +++++++++++++++------------------- Objects/codeobject.c | 44 +++++++++------------- Programs/test_frozenmain.h | 33 ++++++----------- Python/compile.c | 1 - Python/marshal.c | 7 ---- Tools/scripts/deepfreeze.py | 2 - Tools/scripts/umarshal.py | 1 - 10 files changed, 74 insertions(+), 114 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 0ee90d32c5eede..3d80808f98666e 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -92,8 +92,6 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_endlinetable; /* bytes object that holds end lineno for \ instructions separated across different \ lines */ \ - PyObject *co_columntable; /* bytes object that holds start/end column \ - offset each instruction */ \ \ PyObject *co_locationtable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ @@ -154,13 +152,13 @@ PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *, PyObject *); /* same as struct above */ /* Creates a new empty code object with the specified source location. */ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 9d51d0ef189006..f382def3b85cbc 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -177,7 +177,6 @@ struct _PyCodeConstructor { int firstlineno; PyObject *linetable; PyObject *endlinetable; - PyObject *columntable; PyObject *locationtable; /* used by the code */ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 69bea0ac550d10..cccdc65e58c002 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -232,9 +232,8 @@ def func(): pass co.co_firstlineno, co.co_lnotab, co.co_endlinetable, - co.co_columntable, - co.co_exceptiontable, co.co_locationtable, + co.co_exceptiontable, co.co_freevars, co.co_cellvars) @@ -275,7 +274,6 @@ def func2(): ("co_name", "newname"), ("co_linetable", code2.co_linetable), ("co_endlinetable", code2.co_endlinetable), - ("co_columntable", code2.co_columntable), ): with self.subTest(attr=attr, value=value): new_code = code.replace(**{attr: value}) @@ -314,9 +312,8 @@ def func(): co.co_firstlineno, co.co_lnotab, co.co_endlinetable, - co.co_columntable, - co.co_exceptiontable, co.co_locationtable, + co.co_exceptiontable, co.co_freevars, co.co_cellvars, ) @@ -393,14 +390,17 @@ def test_co_positions_artificial_instructions(self): ) def test_endline_and_columntable_none_when_no_debug_ranges(self): - # Make sure that if `-X no_debug_ranges` is used, the endlinetable and - # columntable are None. + # Make sure that if `-X no_debug_ranges` is used, there is + # minimal debug info code = textwrap.dedent(""" def f(): pass - assert f.__code__.co_endlinetable is None - assert f.__code__.co_columntable is None + positions = f.__code__.co_positions() + for line, end_line, column, end_column in positions: + assert line == end_line + assert column is None + assert end_column is None """) assert_python_ok('-X', 'no_debug_ranges', '-c', code) @@ -410,8 +410,11 @@ def test_endline_and_columntable_none_when_no_debug_ranges_env(self): def f(): pass - assert f.__code__.co_endlinetable is None - assert f.__code__.co_columntable is None + positions = f.__code__.co_positions() + for line, end_line, column, end_column in positions: + assert line == end_line + assert column is None + assert end_column is None """) assert_python_ok('-c', code, PYTHONNODEBUGRANGES='1') diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index 43db2aa98c5732..d703765a53e1cb 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -5,8 +5,8 @@ preserve PyDoc_STRVAR(code_new__doc__, "code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n" " flags, codestring, constants, names, varnames, filename, name,\n" -" qualname, firstlineno, linetable, endlinetable, columntable,\n" -" locationtable, exceptiontable, freevars=(), cellvars=(), /)\n" +" qualname, firstlineno, linetable, endlinetable, locationtable,\n" +" exceptiontable, freevars=(), cellvars=(), /)\n" "--\n" "\n" "Create a code object. Not for the faint of heart."); @@ -17,9 +17,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *endlinetable, PyObject *columntable, - PyObject *locationtable, PyObject *exceptiontable, - PyObject *freevars, PyObject *cellvars); + PyObject *endlinetable, PyObject *locationtable, + PyObject *exceptiontable, PyObject *freevars, + PyObject *cellvars); static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -41,7 +41,6 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) int firstlineno; PyObject *linetable; PyObject *endlinetable; - PyObject *columntable; PyObject *locationtable; PyObject *exceptiontable; PyObject *freevars = NULL; @@ -52,7 +51,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) !_PyArg_NoKeywords("code", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 19, 21)) { + if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 18, 20)) { goto exit; } argcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); @@ -133,31 +132,30 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } linetable = PyTuple_GET_ITEM(args, 14); endlinetable = PyTuple_GET_ITEM(args, 15); - columntable = PyTuple_GET_ITEM(args, 16); - locationtable = PyTuple_GET_ITEM(args, 17); - if (!PyBytes_Check(PyTuple_GET_ITEM(args, 18))) { - _PyArg_BadArgument("code", "argument 19", "bytes", PyTuple_GET_ITEM(args, 18)); + locationtable = PyTuple_GET_ITEM(args, 16); + if (!PyBytes_Check(PyTuple_GET_ITEM(args, 17))) { + _PyArg_BadArgument("code", "argument 18", "bytes", PyTuple_GET_ITEM(args, 17)); goto exit; } - exceptiontable = PyTuple_GET_ITEM(args, 18); - if (PyTuple_GET_SIZE(args) < 20) { + exceptiontable = PyTuple_GET_ITEM(args, 17); + if (PyTuple_GET_SIZE(args) < 19) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 19))) { - _PyArg_BadArgument("code", "argument 20", "tuple", PyTuple_GET_ITEM(args, 19)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 18))) { + _PyArg_BadArgument("code", "argument 19", "tuple", PyTuple_GET_ITEM(args, 18)); goto exit; } - freevars = PyTuple_GET_ITEM(args, 19); - if (PyTuple_GET_SIZE(args) < 21) { + freevars = PyTuple_GET_ITEM(args, 18); + if (PyTuple_GET_SIZE(args) < 20) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 20))) { - _PyArg_BadArgument("code", "argument 21", "tuple", PyTuple_GET_ITEM(args, 20)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 19))) { + _PyArg_BadArgument("code", "argument 20", "tuple", PyTuple_GET_ITEM(args, 19)); goto exit; } - cellvars = PyTuple_GET_ITEM(args, 20); + cellvars = PyTuple_GET_ITEM(args, 19); skip_optional: - return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, endlinetable, columntable, locationtable, exceptiontable, freevars, cellvars); + return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, endlinetable, locationtable, exceptiontable, freevars, cellvars); exit: return return_value; @@ -170,8 +168,7 @@ PyDoc_STRVAR(code_replace__doc__, " co_names=None, co_varnames=None, co_freevars=None,\n" " co_cellvars=None, co_filename=None, co_name=None,\n" " co_qualname=None, co_linetable=None, co_endlinetable=None,\n" -" co_columntable=None, co_locationtable=None,\n" -" co_exceptiontable=None)\n" +" co_locationtable=None, co_exceptiontable=None)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -189,16 +186,16 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_columntable, PyObject *co_locationtable, + PyObject *co_locationtable, PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_endlinetable", "co_columntable", "co_locationtable", "co_exceptiontable", NULL}; + static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_endlinetable", "co_locationtable", "co_exceptiontable", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "replace", 0}; - PyObject *argsbuf[21]; + PyObject *argsbuf[20]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int co_argcount = self->co_argcount; int co_posonlyargcount = self->co_posonlyargcount; @@ -218,7 +215,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_qualname = self->co_qualname; PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; PyObject *co_endlinetable = self->co_endlinetable; - PyObject *co_columntable = self->co_columntable; PyObject *co_locationtable = self->co_locationtable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; @@ -408,24 +404,18 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } } if (args[18]) { - co_columntable = args[18]; - if (!--noptargs) { - goto skip_optional_kwonly; - } - } - if (args[19]) { - co_locationtable = args[19]; + co_locationtable = args[18]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (!PyBytes_Check(args[20])) { - _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[20]); + if (!PyBytes_Check(args[19])) { + _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[19]); goto exit; } - co_exceptiontable = (PyBytesObject *)args[20]; + co_exceptiontable = (PyBytesObject *)args[19]; skip_optional_kwonly: - return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_endlinetable, co_columntable, co_locationtable, co_exceptiontable); + return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_endlinetable, co_locationtable, co_exceptiontable); exit: return return_value; @@ -467,4 +457,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=f4f0290f1ff3c264 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=49518e9b9b48880a input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 5e4fe412e7c5db..24f9b06fdaee37 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -246,8 +246,8 @@ _PyCode_Validate(struct _PyCodeConstructor *con) con->linetable == NULL || !PyBytes_Check(con->linetable) || con->endlinetable == NULL || (con->endlinetable != Py_None && !PyBytes_Check(con->endlinetable)) || - con->columntable == NULL || - (con->columntable != Py_None && !PyBytes_Check(con->columntable)) || + con->locationtable == NULL || + (con->locationtable != Py_None && !PyBytes_Check(con->locationtable)) || con->exceptiontable == NULL || !PyBytes_Check(con->exceptiontable) ) { PyErr_BadInternalCall(); @@ -309,8 +309,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_linetable = con->linetable; Py_INCREF(con->endlinetable); co->co_endlinetable = con->endlinetable; - Py_INCREF(con->columntable); - co->co_columntable = con->columntable; Py_INCREF(con->locationtable); co->co_locationtable = con->locationtable; @@ -488,7 +486,7 @@ _PyCode_New(struct _PyCodeConstructor *con) } PyObject *replacement_locations = NULL; - // Discard the endlinetable and columntable if we are opted out of debug + // Compact the locationtable if we are opted out of debug // ranges. if (!_Py_GetConfig()->code_debug_ranges) { replacement_locations = remove_column_info(con->locationtable); @@ -522,7 +520,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, - PyObject *columntable, PyObject *locationtable, + PyObject *locationtable, PyObject *exceptiontable) { PyCodeObject *co = NULL; @@ -602,7 +600,6 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, .firstlineno = firstlineno, .linetable = linetable, .endlinetable = endlinetable, - .columntable = columntable, .locationtable = locationtable, .consts = consts, @@ -649,14 +646,14 @@ PyCode_New(int argcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *endlinetable, - PyObject *columntable, PyObject *locationtable, + PyObject *locationtable, PyObject *exceptiontable) { return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable, - endlinetable, columntable, locationtable, + endlinetable, locationtable, exceptiontable); } @@ -690,7 +687,6 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .firstlineno = firstlineno, .linetable = emptystring, .endlinetable = emptystring, - .columntable = emptystring, .locationtable = emptystring, .consts = nulltuple, .names = nulltuple, @@ -1424,7 +1420,6 @@ code.__new__ as code_new firstlineno: int linetable: object(subclass_of="&PyBytes_Type") endlinetable: object - columntable: object locationtable: object exceptiontable: object(subclass_of="&PyBytes_Type") freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () @@ -1440,10 +1435,10 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *endlinetable, PyObject *columntable, - PyObject *locationtable, PyObject *exceptiontable, - PyObject *freevars, PyObject *cellvars) -/*[clinic end generated code: output=25d03c9913c257fd input=e4d7c37197830e7f]*/ + PyObject *endlinetable, PyObject *locationtable, + PyObject *exceptiontable, PyObject *freevars, + PyObject *cellvars) +/*[clinic end generated code: output=dbd73788cdc93d56 input=b803baf5513eecd1]*/ { PyObject *co = NULL; PyObject *ournames = NULL; @@ -1489,9 +1484,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, "code: endlinetable must be None or bytes"); goto cleanup; } - if (!Py_IsNone(columntable) && !PyBytes_Check(columntable)) { + if (!Py_IsNone(locationtable) && !PyBytes_Check(locationtable)) { PyErr_SetString(PyExc_ValueError, - "code: columntable must be None or bytes"); + "code: locationtable must be None or bytes"); goto cleanup; } @@ -1522,7 +1517,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, ourcellvars, filename, name, qualname, firstlineno, linetable, endlinetable, - columntable, locationtable, + locationtable, exceptiontable ); cleanup: @@ -1560,7 +1555,6 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_qualname); Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_endlinetable); - Py_XDECREF(co->co_columntable); Py_XDECREF(co->co_locationtable); Py_XDECREF(co->co_exceptiontable); if (co->co_weakreflist != NULL) { @@ -1712,7 +1706,6 @@ static PyMemberDef code_memberlist[] = { {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, {"co_endlinetable", T_OBJECT, OFF(co_endlinetable), READONLY}, - {"co_columntable", T_OBJECT, OFF(co_columntable), READONLY}, {"co_locationtable", T_OBJECT, OFF(co_locationtable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ @@ -1810,7 +1803,6 @@ code.replace co_qualname: unicode(c_default="self->co_qualname") = None co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None co_endlinetable: object(c_default="self->co_endlinetable") = None - co_columntable: object(c_default="self->co_columntable") = None co_locationtable: object(c_default="self->co_locationtable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None @@ -1827,9 +1819,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_columntable, PyObject *co_locationtable, + PyObject *co_locationtable, PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=4d1492921dd31aef input=3e19c37f55c87e86]*/ +/*[clinic end generated code: output=38cfe38ae5d3f341 input=33f50be85d1286b1]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1895,9 +1887,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, "co_endlinetable must be None or bytes"); goto error; } - if (!Py_IsNone(co_columntable) && !PyBytes_Check(co_columntable)) { + if (!Py_IsNone(co_locationtable) && !PyBytes_Check(co_locationtable)) { PyErr_SetString(PyExc_ValueError, - "co_columntable must be None or bytes"); + "co_locationtable must be None or bytes"); goto error; } @@ -1906,7 +1898,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_firstlineno, (PyObject*)co_linetable, - (PyObject*)co_endlinetable, (PyObject*)co_columntable, + (PyObject*)co_endlinetable, (PyObject*)co_locationtable, (PyObject*)co_exceptiontable); error: diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index e9895b53f83d6a..f471ac5653b062 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -31,26 +31,15 @@ unsigned char M_test_frozenmain[] = { 0,115,18,0,0,0,2,128,8,3,8,1,22,2,34,1, 42,1,8,1,48,7,4,249,115,20,0,0,0,2,128,8, 3,8,1,22,2,34,1,42,1,2,7,4,1,2,249,52, - 7,115,176,0,0,0,0,0,1,11,1,11,1,11,1,11, - 1,25,1,25,1,25,1,25,1,6,1,6,7,27,1,28, - 1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,6, - 1,6,7,17,19,22,19,27,19,27,19,27,19,27,19,27, - 1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28, - 10,39,10,27,10,39,10,39,10,39,10,39,10,39,10,41, - 10,41,10,41,10,41,10,41,10,41,10,41,42,50,10,51, - 10,51,10,51,10,51,10,51,1,7,12,2,1,42,1,42, - 5,8,5,10,5,10,11,41,21,24,11,41,11,41,28,34, - 35,38,28,39,28,39,28,39,28,39,28,39,11,41,11,41, - 5,42,5,42,5,42,5,42,5,42,5,42,5,42,5,42, - 5,42,1,42,1,42,243,152,0,0,0,248,240,6,0,1, - 11,128,10,128,10,128,10,216,0,24,208,0,24,208,0,24, - 208,0,24,224,0,5,128,5,208,6,26,209,0,27,212,0, - 27,208,0,27,216,0,5,128,5,128,106,144,35,148,40,209, - 0,27,212,0,27,208,0,27,216,9,38,208,9,26,212,9, - 38,209,9,40,212,9,40,168,24,212,9,50,128,6,240,2, - 6,12,2,240,0,7,1,42,240,0,7,1,42,128,67,240, - 14,0,5,10,128,69,208,10,40,144,67,208,10,40,208,10, - 40,152,54,160,35,156,59,208,10,40,208,10,40,209,4,41, - 212,4,41,208,4,41,208,4,41,240,15,7,1,42,240,0, - 7,1,42,114,9,0,0,0, + 7,115,152,0,0,0,248,240,6,0,1,11,128,10,128,10, + 128,10,216,0,24,208,0,24,208,0,24,208,0,24,224,0, + 5,128,5,208,6,26,209,0,27,212,0,27,208,0,27,216, + 0,5,128,5,128,106,144,35,148,40,209,0,27,212,0,27, + 208,0,27,216,9,38,208,9,26,212,9,38,209,9,40,212, + 9,40,168,24,212,9,50,128,6,240,2,6,12,2,240,0, + 7,1,42,240,0,7,1,42,128,67,240,14,0,5,10,128, + 69,208,10,40,144,67,208,10,40,208,10,40,152,54,160,35, + 156,59,208,10,40,208,10,40,209,4,41,212,4,41,208,4, + 41,208,4,41,240,15,7,1,42,240,0,7,1,42,114,9, + 0,0,0, }; diff --git a/Python/compile.c b/Python/compile.c index 79e009d300c1ed..efdf4d67b31fa9 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8068,7 +8068,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist, .firstlineno = c->u->u_firstlineno, .linetable = a->a_lnotab, .endlinetable = a->a_enotab, - .columntable = a->a_cnotab, .locationtable = a->a_locationtable, .consts = consts, diff --git a/Python/marshal.c b/Python/marshal.c index cc29d5c0339bc8..d8141dc7be6896 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -565,7 +565,6 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_long(co->co_firstlineno, p); w_object(co->co_linetable, p); w_object(co->co_endlinetable, p); - w_object(co->co_columntable, p); w_object(co->co_locationtable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); @@ -1360,7 +1359,6 @@ r_object(RFILE *p) int firstlineno; PyObject *linetable = NULL; PyObject* endlinetable = NULL; - PyObject* columntable = NULL; PyObject* locationtable = NULL; PyObject *exceptiontable = NULL; @@ -1420,9 +1418,6 @@ r_object(RFILE *p) endlinetable = r_object(p); if (endlinetable == NULL) goto code_error; - columntable = r_object(p); - if (columntable == NULL) - goto code_error; locationtable = r_object(p); if (locationtable == NULL) goto code_error; @@ -1440,7 +1435,6 @@ r_object(RFILE *p) .firstlineno = firstlineno, .linetable = linetable, .endlinetable = endlinetable, - .columntable = columntable, .locationtable = locationtable, .consts = consts, @@ -1480,7 +1474,6 @@ r_object(RFILE *p) Py_XDECREF(qualname); Py_XDECREF(linetable); Py_XDECREF(endlinetable); - Py_XDECREF(columntable); Py_XDECREF(locationtable); Py_XDECREF(exceptiontable); } diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index 2182d810abf53a..d547eafa900b54 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -239,7 +239,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_qualname = self.generate(name + "_qualname", code.co_qualname) co_linetable = self.generate(name + "_linetable", code.co_linetable) co_endlinetable = self.generate(name + "_endlinetable", code.co_endlinetable) - co_columntable = self.generate(name + "_columntable", code.co_columntable) co_locationtable = self.generate(name + "_locationtable", code.co_locationtable) co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable) # These fields are not directly accessible @@ -280,7 +279,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f".co_qualname = {co_qualname},") self.write(f".co_linetable = {co_linetable},") self.write(f".co_endlinetable = {co_endlinetable},") - self.write(f".co_columntable = {co_columntable},") self.write(f".co_locationtable = {co_locationtable},") self.write(f".co_code_adaptive = {co_code_adaptive},") name_as_code = f"(PyCodeObject *)&{name}" diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index 3ab3b000616ec7..41180beac8802a 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -290,7 +290,6 @@ def R_REF(obj: Any) -> Any: retval.co_firstlineno = self.r_long() retval.co_linetable = self.r_object() retval.co_endlinetable = self.r_object() - retval.co_columntable = self.r_object() retval.co_locationtable = self.r_object() retval.co_exceptiontable = self.r_object() return retval From 6f69354fc24aaee0e39a14aebcab51594a172ed7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Apr 2022 17:15:49 +0100 Subject: [PATCH 04/18] Remove end-line table from code objects. --- Include/cpython/code.h | 8 +--- Include/internal/pycore_code.h | 1 - Lib/test/test_code.py | 3 -- Lib/test/test_marshal.py | 2 +- Objects/clinic/codeobject.c.h | 73 +++++++++++++++------------------- Objects/codeobject.c | 52 +++++------------------- Programs/test_frozenmain.h | 24 +++++------ Python/compile.c | 1 - Python/marshal.c | 7 ---- Tools/scripts/deepfreeze.py | 2 - Tools/scripts/umarshal.py | 1 - 11 files changed, 58 insertions(+), 116 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 3d80808f98666e..f66ac159f5986d 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -89,10 +89,6 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_linetable; /* bytes (encoding addr<->lineno mapping) \ See Objects/lnotab_notes.txt for details. \ */ \ - PyObject *co_endlinetable; /* bytes object that holds end lineno for \ - instructions separated across different \ - lines */ \ - \ PyObject *co_locationtable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ /* Scratch space for extra data relating to the code object. \ @@ -152,13 +148,13 @@ PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *); PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *, PyObject *); + PyObject *, PyObject *); /* same as struct above */ /* Creates a new empty code object with the specified source location. */ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index f382def3b85cbc..02ff7c2f9a83a4 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -176,7 +176,6 @@ struct _PyCodeConstructor { PyObject *code; int firstlineno; PyObject *linetable; - PyObject *endlinetable; PyObject *locationtable; /* used by the code */ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index cccdc65e58c002..384f065dd1b71b 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -231,7 +231,6 @@ def func(): pass co.co_qualname, co.co_firstlineno, co.co_lnotab, - co.co_endlinetable, co.co_locationtable, co.co_exceptiontable, co.co_freevars, @@ -273,7 +272,6 @@ def func2(): ("co_filename", "newfilename"), ("co_name", "newname"), ("co_linetable", code2.co_linetable), - ("co_endlinetable", code2.co_endlinetable), ): with self.subTest(attr=attr, value=value): new_code = code.replace(**{attr: value}) @@ -311,7 +309,6 @@ def func(): co.co_qualname, co.co_firstlineno, co.co_lnotab, - co.co_endlinetable, co.co_locationtable, co.co_exceptiontable, co.co_freevars, diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 2f1462f0e85ccf..959b4e5d93ba66 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -129,7 +129,7 @@ def test_different_filenames(self): self.assertEqual(co2.co_filename, "f2") @requires_debug_ranges() - def test_no_columntable_and_endlinetable_with_no_debug_ranges(self): + def test_minimal_locationtable_with_no_debug_ranges(self): # Make sure when demarshalling objects with `-X no_debug_ranges` # that the columns are None. co = ExceptionTestCase.test_exceptions.__code__ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index d703765a53e1cb..e0372ae5937319 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -5,8 +5,8 @@ preserve PyDoc_STRVAR(code_new__doc__, "code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n" " flags, codestring, constants, names, varnames, filename, name,\n" -" qualname, firstlineno, linetable, endlinetable, locationtable,\n" -" exceptiontable, freevars=(), cellvars=(), /)\n" +" qualname, firstlineno, linetable, locationtable, exceptiontable,\n" +" freevars=(), cellvars=(), /)\n" "--\n" "\n" "Create a code object. Not for the faint of heart."); @@ -17,9 +17,8 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *endlinetable, PyObject *locationtable, - PyObject *exceptiontable, PyObject *freevars, - PyObject *cellvars); + PyObject *locationtable, PyObject *exceptiontable, + PyObject *freevars, PyObject *cellvars); static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -40,7 +39,6 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *qualname; int firstlineno; PyObject *linetable; - PyObject *endlinetable; PyObject *locationtable; PyObject *exceptiontable; PyObject *freevars = NULL; @@ -51,7 +49,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) !_PyArg_NoKeywords("code", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 18, 20)) { + if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 17, 19)) { goto exit; } argcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); @@ -131,31 +129,34 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } linetable = PyTuple_GET_ITEM(args, 14); - endlinetable = PyTuple_GET_ITEM(args, 15); - locationtable = PyTuple_GET_ITEM(args, 16); - if (!PyBytes_Check(PyTuple_GET_ITEM(args, 17))) { - _PyArg_BadArgument("code", "argument 18", "bytes", PyTuple_GET_ITEM(args, 17)); + if (!PyBytes_Check(PyTuple_GET_ITEM(args, 15))) { + _PyArg_BadArgument("code", "argument 16", "bytes", PyTuple_GET_ITEM(args, 15)); goto exit; } - exceptiontable = PyTuple_GET_ITEM(args, 17); - if (PyTuple_GET_SIZE(args) < 19) { + locationtable = PyTuple_GET_ITEM(args, 15); + if (!PyBytes_Check(PyTuple_GET_ITEM(args, 16))) { + _PyArg_BadArgument("code", "argument 17", "bytes", PyTuple_GET_ITEM(args, 16)); + goto exit; + } + exceptiontable = PyTuple_GET_ITEM(args, 16); + if (PyTuple_GET_SIZE(args) < 18) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 18))) { - _PyArg_BadArgument("code", "argument 19", "tuple", PyTuple_GET_ITEM(args, 18)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 17))) { + _PyArg_BadArgument("code", "argument 18", "tuple", PyTuple_GET_ITEM(args, 17)); goto exit; } - freevars = PyTuple_GET_ITEM(args, 18); - if (PyTuple_GET_SIZE(args) < 20) { + freevars = PyTuple_GET_ITEM(args, 17); + if (PyTuple_GET_SIZE(args) < 19) { goto skip_optional; } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 19))) { - _PyArg_BadArgument("code", "argument 20", "tuple", PyTuple_GET_ITEM(args, 19)); + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 18))) { + _PyArg_BadArgument("code", "argument 19", "tuple", PyTuple_GET_ITEM(args, 18)); goto exit; } - cellvars = PyTuple_GET_ITEM(args, 19); + cellvars = PyTuple_GET_ITEM(args, 18); skip_optional: - return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, endlinetable, locationtable, exceptiontable, freevars, cellvars); + return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, locationtable, exceptiontable, freevars, cellvars); exit: return return_value; @@ -167,8 +168,8 @@ PyDoc_STRVAR(code_replace__doc__, " co_flags=-1, co_firstlineno=-1, co_code=None, co_consts=None,\n" " co_names=None, co_varnames=None, co_freevars=None,\n" " co_cellvars=None, co_filename=None, co_name=None,\n" -" co_qualname=None, co_linetable=None, co_endlinetable=None,\n" -" co_locationtable=None, co_exceptiontable=None)\n" +" co_qualname=None, co_linetable=None, co_locationtable=None,\n" +" co_exceptiontable=None)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -185,17 +186,16 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_locationtable, + PyBytesObject *co_linetable, PyObject *co_locationtable, PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_endlinetable", "co_locationtable", "co_exceptiontable", NULL}; + static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_locationtable", "co_exceptiontable", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "replace", 0}; - PyObject *argsbuf[20]; + PyObject *argsbuf[19]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int co_argcount = self->co_argcount; int co_posonlyargcount = self->co_posonlyargcount; @@ -214,7 +214,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_name = self->co_name; PyObject *co_qualname = self->co_qualname; PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; - PyObject *co_endlinetable = self->co_endlinetable; PyObject *co_locationtable = self->co_locationtable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; @@ -398,24 +397,18 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } } if (args[17]) { - co_endlinetable = args[17]; - if (!--noptargs) { - goto skip_optional_kwonly; - } - } - if (args[18]) { - co_locationtable = args[18]; + co_locationtable = args[17]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (!PyBytes_Check(args[19])) { - _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[19]); + if (!PyBytes_Check(args[18])) { + _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[18]); goto exit; } - co_exceptiontable = (PyBytesObject *)args[19]; + co_exceptiontable = (PyBytesObject *)args[18]; skip_optional_kwonly: - return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_endlinetable, co_locationtable, co_exceptiontable); + return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_locationtable, co_exceptiontable); exit: return return_value; @@ -457,4 +450,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=49518e9b9b48880a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d4f25a65102bb3a4 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 24f9b06fdaee37..fcad58c957eb60 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -244,10 +244,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) con->qualname == NULL || !PyUnicode_Check(con->qualname) || con->filename == NULL || !PyUnicode_Check(con->filename) || con->linetable == NULL || !PyBytes_Check(con->linetable) || - con->endlinetable == NULL || - (con->endlinetable != Py_None && !PyBytes_Check(con->endlinetable)) || - con->locationtable == NULL || - (con->locationtable != Py_None && !PyBytes_Check(con->locationtable)) || + con->locationtable == NULL || !PyBytes_Check(con->locationtable) || con->exceptiontable == NULL || !PyBytes_Check(con->exceptiontable) ) { PyErr_BadInternalCall(); @@ -307,8 +304,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_firstlineno = con->firstlineno; Py_INCREF(con->linetable); co->co_linetable = con->linetable; - Py_INCREF(con->endlinetable); - co->co_endlinetable = con->endlinetable; Py_INCREF(con->locationtable); co->co_locationtable = con->locationtable; @@ -519,7 +514,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, - PyObject *linetable, PyObject *endlinetable, + PyObject *linetable, PyObject *locationtable, PyObject *exceptiontable) { @@ -599,7 +594,6 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, .code = code, .firstlineno = firstlineno, .linetable = linetable, - .endlinetable = endlinetable, .locationtable = locationtable, .consts = consts, @@ -645,7 +639,7 @@ PyCode_New(int argcount, int kwonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, - int firstlineno, PyObject *linetable, PyObject *endlinetable, + int firstlineno, PyObject *linetable, PyObject *locationtable, PyObject *exceptiontable) { @@ -653,7 +647,7 @@ PyCode_New(int argcount, int kwonlyargcount, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable, - endlinetable, locationtable, + locationtable, exceptiontable); } @@ -686,7 +680,6 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .code = emptystring, .firstlineno = firstlineno, .linetable = emptystring, - .endlinetable = emptystring, .locationtable = emptystring, .consts = nulltuple, .names = nulltuple, @@ -1419,8 +1412,7 @@ code.__new__ as code_new qualname: unicode firstlineno: int linetable: object(subclass_of="&PyBytes_Type") - endlinetable: object - locationtable: object + locationtable: object(subclass_of="&PyBytes_Type") exceptiontable: object(subclass_of="&PyBytes_Type") freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () @@ -1435,10 +1427,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *endlinetable, PyObject *locationtable, - PyObject *exceptiontable, PyObject *freevars, - PyObject *cellvars) -/*[clinic end generated code: output=dbd73788cdc93d56 input=b803baf5513eecd1]*/ + PyObject *locationtable, PyObject *exceptiontable, + PyObject *freevars, PyObject *cellvars) +/*[clinic end generated code: output=39366b579671611f input=0f13a7fa18497323]*/ { PyObject *co = NULL; PyObject *ournames = NULL; @@ -1479,17 +1470,6 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, goto cleanup; } - if (!Py_IsNone(endlinetable) && !PyBytes_Check(endlinetable)) { - PyErr_SetString(PyExc_ValueError, - "code: endlinetable must be None or bytes"); - goto cleanup; - } - if (!Py_IsNone(locationtable) && !PyBytes_Check(locationtable)) { - PyErr_SetString(PyExc_ValueError, - "code: locationtable must be None or bytes"); - goto cleanup; - } - ournames = validate_and_copy_tuple(names); if (ournames == NULL) goto cleanup; @@ -1516,7 +1496,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, ourvarnames, ourfreevars, ourcellvars, filename, name, qualname, firstlineno, - linetable, endlinetable, + linetable, locationtable, exceptiontable ); @@ -1554,7 +1534,6 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_name); Py_XDECREF(co->co_qualname); Py_XDECREF(co->co_linetable); - Py_XDECREF(co->co_endlinetable); Py_XDECREF(co->co_locationtable); Py_XDECREF(co->co_exceptiontable); if (co->co_weakreflist != NULL) { @@ -1705,7 +1684,6 @@ static PyMemberDef code_memberlist[] = { {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, - {"co_endlinetable", T_OBJECT, OFF(co_endlinetable), READONLY}, {"co_locationtable", T_OBJECT, OFF(co_locationtable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ @@ -1802,7 +1780,6 @@ code.replace co_name: unicode(c_default="self->co_name") = None co_qualname: unicode(c_default="self->co_qualname") = None co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None - co_endlinetable: object(c_default="self->co_endlinetable") = None co_locationtable: object(c_default="self->co_locationtable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None @@ -1818,10 +1795,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, PyObject *co_endlinetable, - PyObject *co_locationtable, + PyBytesObject *co_linetable, PyObject *co_locationtable, PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=38cfe38ae5d3f341 input=33f50be85d1286b1]*/ +/*[clinic end generated code: output=982eac45e7d850be input=d438e73e253965eb]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1882,11 +1858,6 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_freevars = freevars; } - if (!Py_IsNone(co_endlinetable) && !PyBytes_Check(co_endlinetable)) { - PyErr_SetString(PyExc_ValueError, - "co_endlinetable must be None or bytes"); - goto error; - } if (!Py_IsNone(co_locationtable) && !PyBytes_Check(co_locationtable)) { PyErr_SetString(PyExc_ValueError, "co_locationtable must be None or bytes"); @@ -1898,7 +1869,6 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_firstlineno, (PyObject*)co_linetable, - (PyObject*)co_endlinetable, (PyObject*)co_locationtable, (PyObject*)co_exceptiontable); error: diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index f471ac5653b062..b7499efdcf3507 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -29,17 +29,15 @@ unsigned char M_test_frozenmain[] = { 95,102,114,111,122,101,110,109,97,105,110,46,112,121,250,8, 60,109,111,100,117,108,101,62,114,11,0,0,0,1,0,0, 0,115,18,0,0,0,2,128,8,3,8,1,22,2,34,1, - 42,1,8,1,48,7,4,249,115,20,0,0,0,2,128,8, - 3,8,1,22,2,34,1,42,1,2,7,4,1,2,249,52, - 7,115,152,0,0,0,248,240,6,0,1,11,128,10,128,10, - 128,10,216,0,24,208,0,24,208,0,24,208,0,24,224,0, - 5,128,5,208,6,26,209,0,27,212,0,27,208,0,27,216, - 0,5,128,5,128,106,144,35,148,40,209,0,27,212,0,27, - 208,0,27,216,9,38,208,9,26,212,9,38,209,9,40,212, - 9,40,168,24,212,9,50,128,6,240,2,6,12,2,240,0, - 7,1,42,240,0,7,1,42,128,67,240,14,0,5,10,128, - 69,208,10,40,144,67,208,10,40,208,10,40,152,54,160,35, - 156,59,208,10,40,208,10,40,209,4,41,212,4,41,208,4, - 41,208,4,41,240,15,7,1,42,240,0,7,1,42,114,9, - 0,0,0, + 42,1,8,1,48,7,4,249,115,152,0,0,0,248,240,6, + 0,1,11,128,10,128,10,128,10,216,0,24,208,0,24,208, + 0,24,208,0,24,224,0,5,128,5,208,6,26,209,0,27, + 212,0,27,208,0,27,216,0,5,128,5,128,106,144,35,148, + 40,209,0,27,212,0,27,208,0,27,216,9,38,208,9,26, + 212,9,38,209,9,40,212,9,40,168,24,212,9,50,128,6, + 240,2,6,12,2,240,0,7,1,42,240,0,7,1,42,128, + 67,240,14,0,5,10,128,69,208,10,40,144,67,208,10,40, + 208,10,40,152,54,160,35,156,59,208,10,40,208,10,40,209, + 4,41,212,4,41,208,4,41,208,4,41,240,15,7,1,42, + 240,0,7,1,42,114,9,0,0,0, }; diff --git a/Python/compile.c b/Python/compile.c index efdf4d67b31fa9..0b1472b2fa054a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8067,7 +8067,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist, .code = a->a_bytecode, .firstlineno = c->u->u_firstlineno, .linetable = a->a_lnotab, - .endlinetable = a->a_enotab, .locationtable = a->a_locationtable, .consts = consts, diff --git a/Python/marshal.c b/Python/marshal.c index d8141dc7be6896..7b25f65f82e88c 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -564,7 +564,6 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(co->co_qualname, p); w_long(co->co_firstlineno, p); w_object(co->co_linetable, p); - w_object(co->co_endlinetable, p); w_object(co->co_locationtable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); @@ -1358,7 +1357,6 @@ r_object(RFILE *p) PyObject *qualname = NULL; int firstlineno; PyObject *linetable = NULL; - PyObject* endlinetable = NULL; PyObject* locationtable = NULL; PyObject *exceptiontable = NULL; @@ -1415,9 +1413,6 @@ r_object(RFILE *p) linetable = r_object(p); if (linetable == NULL) goto code_error; - endlinetable = r_object(p); - if (endlinetable == NULL) - goto code_error; locationtable = r_object(p); if (locationtable == NULL) goto code_error; @@ -1434,7 +1429,6 @@ r_object(RFILE *p) .code = code, .firstlineno = firstlineno, .linetable = linetable, - .endlinetable = endlinetable, .locationtable = locationtable, .consts = consts, @@ -1473,7 +1467,6 @@ r_object(RFILE *p) Py_XDECREF(name); Py_XDECREF(qualname); Py_XDECREF(linetable); - Py_XDECREF(endlinetable); Py_XDECREF(locationtable); Py_XDECREF(exceptiontable); } diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index d547eafa900b54..dea6eadef1f05e 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -238,7 +238,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_name = self.generate(name + "_name", code.co_name) co_qualname = self.generate(name + "_qualname", code.co_qualname) co_linetable = self.generate(name + "_linetable", code.co_linetable) - co_endlinetable = self.generate(name + "_endlinetable", code.co_endlinetable) co_locationtable = self.generate(name + "_locationtable", code.co_locationtable) co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable) # These fields are not directly accessible @@ -278,7 +277,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f".co_name = {co_name},") self.write(f".co_qualname = {co_qualname},") self.write(f".co_linetable = {co_linetable},") - self.write(f".co_endlinetable = {co_endlinetable},") self.write(f".co_locationtable = {co_locationtable},") self.write(f".co_code_adaptive = {co_code_adaptive},") name_as_code = f"(PyCodeObject *)&{name}" diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index 41180beac8802a..f3454e6209e5b7 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -289,7 +289,6 @@ def R_REF(obj: Any) -> Any: retval.co_qualname = self.r_object() retval.co_firstlineno = self.r_long() retval.co_linetable = self.r_object() - retval.co_endlinetable = self.r_object() retval.co_locationtable = self.r_object() retval.co_exceptiontable = self.r_object() return retval From b52930676dbfecc1e2416b2c56f001272e1b6fe3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Apr 2022 17:50:42 +0100 Subject: [PATCH 05/18] Remove line table from code objects. --- Include/cpython/code.h | 9 ++--- Include/internal/pycore_code.h | 1 - Lib/test/test_code.py | 3 -- Lib/test/test_compile.py | 4 +- Objects/clinic/codeobject.c.h | 70 ++++++++++++-------------------- Objects/codeobject.c | 31 +++++--------- Programs/test_frozenmain.h | 23 +++++------ Python/compile.c | 3 +- Python/marshal.c | 7 ---- Tools/gdb/libpython.py | 74 ++++++++++++++++++++++++++++------ Tools/scripts/deepfreeze.py | 2 - Tools/scripts/umarshal.py | 1 - 12 files changed, 114 insertions(+), 114 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index f66ac159f5986d..a8d52133aba874 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -86,10 +86,7 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_filename; /* unicode (where it was loaded from) */ \ PyObject *co_name; /* unicode (name, for reference) */ \ PyObject *co_qualname; /* unicode (qualname, for reference) */ \ - PyObject *co_linetable; /* bytes (encoding addr<->lineno mapping) \ - See Objects/lnotab_notes.txt for details. \ - */ \ - PyObject *co_locationtable; /* bytes object that holds location info */ \ + PyObject *co_locationtable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ /* Scratch space for extra data relating to the code object. \ Type is a void* to keep the format private in codeobject.c to force \ @@ -148,13 +145,13 @@ PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *); + PyObject *); PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *, - PyObject *, PyObject *); + PyObject *); /* same as struct above */ /* Creates a new empty code object with the specified source location. */ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 02ff7c2f9a83a4..630069eb631f8c 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -175,7 +175,6 @@ struct _PyCodeConstructor { /* the code */ PyObject *code; int firstlineno; - PyObject *linetable; PyObject *locationtable; /* used by the code */ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 384f065dd1b71b..a6972935d406e6 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -230,7 +230,6 @@ def func(): pass co.co_name, co.co_qualname, co.co_firstlineno, - co.co_lnotab, co.co_locationtable, co.co_exceptiontable, co.co_freevars, @@ -271,7 +270,6 @@ def func2(): ("co_cellvars", ("cellvar",)), ("co_filename", "newfilename"), ("co_name", "newname"), - ("co_linetable", code2.co_linetable), ): with self.subTest(attr=attr, value=value): new_code = code.replace(**{attr: value}) @@ -308,7 +306,6 @@ def func(): co.co_name, co.co_qualname, co.co_firstlineno, - co.co_lnotab, co.co_locationtable, co.co_exceptiontable, co.co_freevars, diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 5a9c618786f4e2..7ed818e6bbe23a 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -643,7 +643,7 @@ def check_same_constant(const): self.check_constant(f1, frozenset({0})) self.assertTrue(f1(0)) - # Merging equal co_linetable is not a strict requirement + # Merging equal co_locationtable is not a strict requirement # for the Python semantics, it's a more an implementation detail. @support.cpython_only def test_merge_code_attrs(self): @@ -651,7 +651,7 @@ def test_merge_code_attrs(self): f1 = lambda x: x.y.z f2 = lambda a: a.b.c - self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable) + self.assertIs(f1.__code__.co_locationtable, f2.__code__.co_locationtable) # Stripping unused constants is not a strict requirement for the # Python semantics, it's a more an implementation detail. diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index e0372ae5937319..e844187c1d7035 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -5,8 +5,8 @@ preserve PyDoc_STRVAR(code_new__doc__, "code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n" " flags, codestring, constants, names, varnames, filename, name,\n" -" qualname, firstlineno, linetable, locationtable, exceptiontable,\n" -" freevars=(), cellvars=(), /)\n" +" qualname, firstlineno, locationtable, exceptiontable, freevars=(),\n" +" cellvars=(), /)\n" "--\n" "\n" "Create a code object. Not for the faint of heart."); @@ -16,9 +16,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, - PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *locationtable, PyObject *exceptiontable, - PyObject *freevars, PyObject *cellvars); + PyObject *qualname, int firstlineno, PyObject *locationtable, + PyObject *exceptiontable, PyObject *freevars, + PyObject *cellvars); static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -38,7 +38,6 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *name; PyObject *qualname; int firstlineno; - PyObject *linetable; PyObject *locationtable; PyObject *exceptiontable; PyObject *freevars = NULL; @@ -49,7 +48,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) !_PyArg_NoKeywords("code", kwargs)) { goto exit; } - if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 17, 19)) { + if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 16, 18)) { goto exit; } argcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0)); @@ -128,17 +127,20 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) _PyArg_BadArgument("code", "argument 15", "bytes", PyTuple_GET_ITEM(args, 14)); goto exit; } - linetable = PyTuple_GET_ITEM(args, 14); + locationtable = PyTuple_GET_ITEM(args, 14); if (!PyBytes_Check(PyTuple_GET_ITEM(args, 15))) { _PyArg_BadArgument("code", "argument 16", "bytes", PyTuple_GET_ITEM(args, 15)); goto exit; } - locationtable = PyTuple_GET_ITEM(args, 15); - if (!PyBytes_Check(PyTuple_GET_ITEM(args, 16))) { - _PyArg_BadArgument("code", "argument 17", "bytes", PyTuple_GET_ITEM(args, 16)); + exceptiontable = PyTuple_GET_ITEM(args, 15); + if (PyTuple_GET_SIZE(args) < 17) { + goto skip_optional; + } + if (!PyTuple_Check(PyTuple_GET_ITEM(args, 16))) { + _PyArg_BadArgument("code", "argument 17", "tuple", PyTuple_GET_ITEM(args, 16)); goto exit; } - exceptiontable = PyTuple_GET_ITEM(args, 16); + freevars = PyTuple_GET_ITEM(args, 16); if (PyTuple_GET_SIZE(args) < 18) { goto skip_optional; } @@ -146,17 +148,9 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) _PyArg_BadArgument("code", "argument 18", "tuple", PyTuple_GET_ITEM(args, 17)); goto exit; } - freevars = PyTuple_GET_ITEM(args, 17); - if (PyTuple_GET_SIZE(args) < 19) { - goto skip_optional; - } - if (!PyTuple_Check(PyTuple_GET_ITEM(args, 18))) { - _PyArg_BadArgument("code", "argument 19", "tuple", PyTuple_GET_ITEM(args, 18)); - goto exit; - } - cellvars = PyTuple_GET_ITEM(args, 18); + cellvars = PyTuple_GET_ITEM(args, 17); skip_optional: - return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, locationtable, exceptiontable, freevars, cellvars); + return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, locationtable, exceptiontable, freevars, cellvars); exit: return return_value; @@ -168,8 +162,7 @@ PyDoc_STRVAR(code_replace__doc__, " co_flags=-1, co_firstlineno=-1, co_code=None, co_consts=None,\n" " co_names=None, co_varnames=None, co_freevars=None,\n" " co_cellvars=None, co_filename=None, co_name=None,\n" -" co_qualname=None, co_linetable=None, co_locationtable=None,\n" -" co_exceptiontable=None)\n" +" co_qualname=None, co_locationtable=None, co_exceptiontable=None)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -186,16 +179,16 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, PyObject *co_locationtable, + PyObject *co_locationtable, PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_locationtable", "co_exceptiontable", NULL}; + static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_locationtable", "co_exceptiontable", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "replace", 0}; - PyObject *argsbuf[19]; + PyObject *argsbuf[18]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int co_argcount = self->co_argcount; int co_posonlyargcount = self->co_posonlyargcount; @@ -213,7 +206,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_filename = self->co_filename; PyObject *co_name = self->co_name; PyObject *co_qualname = self->co_qualname; - PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; PyObject *co_locationtable = self->co_locationtable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; @@ -387,28 +379,18 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } } if (args[16]) { - if (!PyBytes_Check(args[16])) { - _PyArg_BadArgument("replace", "argument 'co_linetable'", "bytes", args[16]); - goto exit; - } - co_linetable = (PyBytesObject *)args[16]; - if (!--noptargs) { - goto skip_optional_kwonly; - } - } - if (args[17]) { - co_locationtable = args[17]; + co_locationtable = args[16]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (!PyBytes_Check(args[18])) { - _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[18]); + if (!PyBytes_Check(args[17])) { + _PyArg_BadArgument("replace", "argument 'co_exceptiontable'", "bytes", args[17]); goto exit; } - co_exceptiontable = (PyBytesObject *)args[18]; + co_exceptiontable = (PyBytesObject *)args[17]; skip_optional_kwonly: - return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_locationtable, co_exceptiontable); + return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_locationtable, co_exceptiontable); exit: return return_value; @@ -450,4 +432,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=d4f25a65102bb3a4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=82434fea629ecd68 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index fcad58c957eb60..781ec439c60980 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -243,7 +243,6 @@ _PyCode_Validate(struct _PyCodeConstructor *con) con->name == NULL || !PyUnicode_Check(con->name) || con->qualname == NULL || !PyUnicode_Check(con->qualname) || con->filename == NULL || !PyUnicode_Check(con->filename) || - con->linetable == NULL || !PyBytes_Check(con->linetable) || con->locationtable == NULL || !PyBytes_Check(con->locationtable) || con->exceptiontable == NULL || !PyBytes_Check(con->exceptiontable) ) { @@ -302,8 +301,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_flags = con->flags; co->co_firstlineno = con->firstlineno; - Py_INCREF(con->linetable); - co->co_linetable = con->linetable; Py_INCREF(con->locationtable); co->co_locationtable = con->locationtable; @@ -514,7 +511,6 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, - PyObject *linetable, PyObject *locationtable, PyObject *exceptiontable) { @@ -593,7 +589,6 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, .code = code, .firstlineno = firstlineno, - .linetable = linetable, .locationtable = locationtable, .consts = consts, @@ -639,14 +634,14 @@ PyCode_New(int argcount, int kwonlyargcount, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, - int firstlineno, PyObject *linetable, + int firstlineno, PyObject *locationtable, PyObject *exceptiontable) { return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, - name, qualname, firstlineno, linetable, + name, qualname, firstlineno, locationtable, exceptiontable); } @@ -679,7 +674,6 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .qualname = funcname_ob, .code = emptystring, .firstlineno = firstlineno, - .linetable = emptystring, .locationtable = emptystring, .consts = nulltuple, .names = nulltuple, @@ -701,7 +695,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) * source location tracking (co_lines/co_positions) ******************/ -/* Use co_linetable to compute the line number from a bytecode index, addrq. See +/* Use co_locationtable to compute the line number from a bytecode index, addrq. See lnotab_notes.txt for the details of the lnotab representation. */ @@ -1411,7 +1405,6 @@ code.__new__ as code_new name: unicode qualname: unicode firstlineno: int - linetable: object(subclass_of="&PyBytes_Type") locationtable: object(subclass_of="&PyBytes_Type") exceptiontable: object(subclass_of="&PyBytes_Type") freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () @@ -1426,10 +1419,10 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, - PyObject *qualname, int firstlineno, PyObject *linetable, - PyObject *locationtable, PyObject *exceptiontable, - PyObject *freevars, PyObject *cellvars) -/*[clinic end generated code: output=39366b579671611f input=0f13a7fa18497323]*/ + PyObject *qualname, int firstlineno, PyObject *locationtable, + PyObject *exceptiontable, PyObject *freevars, + PyObject *cellvars) +/*[clinic end generated code: output=59e466c5f94e3983 input=f1a94ca09a42b875]*/ { PyObject *co = NULL; PyObject *ournames = NULL; @@ -1496,7 +1489,6 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, ourvarnames, ourfreevars, ourcellvars, filename, name, qualname, firstlineno, - linetable, locationtable, exceptiontable ); @@ -1533,7 +1525,6 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_filename); Py_XDECREF(co->co_name); Py_XDECREF(co->co_qualname); - Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_locationtable); Py_XDECREF(co->co_exceptiontable); if (co->co_weakreflist != NULL) { @@ -1683,7 +1674,6 @@ static PyMemberDef code_memberlist[] = { {"co_name", T_OBJECT, OFF(co_name), READONLY}, {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, {"co_locationtable", T_OBJECT, OFF(co_locationtable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ @@ -1779,7 +1769,6 @@ code.replace co_filename: unicode(c_default="self->co_filename") = None co_name: unicode(c_default="self->co_name") = None co_qualname: unicode(c_default="self->co_qualname") = None - co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None co_locationtable: object(c_default="self->co_locationtable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None @@ -1795,9 +1784,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyBytesObject *co_linetable, PyObject *co_locationtable, + PyObject *co_locationtable, PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=982eac45e7d850be input=d438e73e253965eb]*/ +/*[clinic end generated code: output=7e046bf6eabcd5b2 input=3ba7aa09c21e4be9]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1868,7 +1857,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, - co_qualname, co_firstlineno, (PyObject*)co_linetable, + co_qualname, co_firstlineno, (PyObject*)co_locationtable, (PyObject*)co_exceptiontable); error: diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index b7499efdcf3507..3034927d7a12b9 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -28,16 +28,15 @@ unsigned char M_test_frozenmain[] = { 107,101,121,169,0,243,0,0,0,0,250,18,116,101,115,116, 95,102,114,111,122,101,110,109,97,105,110,46,112,121,250,8, 60,109,111,100,117,108,101,62,114,11,0,0,0,1,0,0, - 0,115,18,0,0,0,2,128,8,3,8,1,22,2,34,1, - 42,1,8,1,48,7,4,249,115,152,0,0,0,248,240,6, - 0,1,11,128,10,128,10,128,10,216,0,24,208,0,24,208, - 0,24,208,0,24,224,0,5,128,5,208,6,26,209,0,27, - 212,0,27,208,0,27,216,0,5,128,5,128,106,144,35,148, - 40,209,0,27,212,0,27,208,0,27,216,9,38,208,9,26, - 212,9,38,209,9,40,212,9,40,168,24,212,9,50,128,6, - 240,2,6,12,2,240,0,7,1,42,240,0,7,1,42,128, - 67,240,14,0,5,10,128,69,208,10,40,144,67,208,10,40, - 208,10,40,152,54,160,35,156,59,208,10,40,208,10,40,209, - 4,41,212,4,41,208,4,41,208,4,41,240,15,7,1,42, - 240,0,7,1,42,114,9,0,0,0, + 0,115,152,0,0,0,248,240,6,0,1,11,128,10,128,10, + 128,10,216,0,24,208,0,24,208,0,24,208,0,24,224,0, + 5,128,5,208,6,26,209,0,27,212,0,27,208,0,27,216, + 0,5,128,5,128,106,144,35,148,40,209,0,27,212,0,27, + 208,0,27,216,9,38,208,9,26,212,9,38,209,9,40,212, + 9,40,168,24,212,9,50,128,6,240,2,6,12,2,240,0, + 7,1,42,240,0,7,1,42,128,67,240,14,0,5,10,128, + 69,208,10,40,144,67,208,10,40,208,10,40,152,54,160,35, + 156,59,208,10,40,208,10,40,209,4,41,212,4,41,208,4, + 41,208,4,41,240,15,7,1,42,240,0,7,1,42,114,9, + 0,0,0, }; diff --git a/Python/compile.c b/Python/compile.c index 0b1472b2fa054a..5bc5b9bfa21bc3 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8066,7 +8066,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist, .code = a->a_bytecode, .firstlineno = c->u->u_firstlineno, - .linetable = a->a_lnotab, .locationtable = a->a_locationtable, .consts = consts, @@ -8543,7 +8542,7 @@ assemble(struct compiler *c, int addNone) if (_PyBytes_Resize(&a.a_locationtable, a.a_location_off) < 0) { goto error; } - if (!merge_const_one(c, &a.a_cnotab)) { + if (!merge_const_one(c, &a.a_locationtable)) { goto error; } if (_PyBytes_Resize(&a.a_bytecode, a.a_offset * sizeof(_Py_CODEUNIT)) < 0) { diff --git a/Python/marshal.c b/Python/marshal.c index 7b25f65f82e88c..612a9595f13649 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -563,7 +563,6 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(co->co_name, p); w_object(co->co_qualname, p); w_long(co->co_firstlineno, p); - w_object(co->co_linetable, p); w_object(co->co_locationtable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); @@ -1356,7 +1355,6 @@ r_object(RFILE *p) PyObject *name = NULL; PyObject *qualname = NULL; int firstlineno; - PyObject *linetable = NULL; PyObject* locationtable = NULL; PyObject *exceptiontable = NULL; @@ -1410,9 +1408,6 @@ r_object(RFILE *p) firstlineno = (int)r_long(p); if (firstlineno == -1 && PyErr_Occurred()) break; - linetable = r_object(p); - if (linetable == NULL) - goto code_error; locationtable = r_object(p); if (locationtable == NULL) goto code_error; @@ -1428,7 +1423,6 @@ r_object(RFILE *p) .code = code, .firstlineno = firstlineno, - .linetable = linetable, .locationtable = locationtable, .consts = consts, @@ -1466,7 +1460,6 @@ r_object(RFILE *p) Py_XDECREF(filename); Py_XDECREF(name); Py_XDECREF(qualname); - Py_XDECREF(linetable); Py_XDECREF(locationtable); Py_XDECREF(exceptiontable); } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 00cdcca084e742..8d0a7034ebfd4d 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -634,6 +634,63 @@ def proxyval(self, visited): else: return BuiltInMethodProxy(ml_name, pyop_m_self) +# Python implementation of location table parsing algorithm +def read(it): + return next(it) + +def read_varint(it): + b = read(it) + val = b & 63; + shift = 0; + while b & 64: + b = read(it) + shift += 6 + val |= (b&63) << shift + return val + +def read_signed_varint(it): + uval = read_varint(it) + if uval & 1: + return -(uval >> 1) + else: + return uval >> 1 + +def parse_location_table(firstlineno, locationtable): + line = firstlineno + addr = 0 + it = iter(locationtable) + while True: + try: + first_byte = read(it) + except StopIteration: + return + code = (first_byte >> 3) & 15 + length = (first_byte & 7) + 1 + end_addr = addr + length + if code == 15: + yield addr, end_addr, None + addr = end_addr + continue + elif code == 14: + line_delta = read_signed_varint(it) + line += line_delta + end_line = line + read_varint(it) + col = read_varint(it) + end_col = read_varint(it) + elif code == 13: # No column + line_delta = read_signed_varint(it) + line += line_delta + elif code in (10, 11, 12): # new line + line_delta = code - 10 + line += line_delta + column = read(it) + end_column = read(it) + else: + assert (0 <= code < 10) + second_byte = read(it) + column = code << 3 | (second_byte >> 4) + yield addr, end_addr, line + addr = end_addr class PyCodeObjectPtr(PyObjectPtr): """ @@ -649,7 +706,7 @@ def addr2line(self, addrq): Analogous to PyCode_Addr2Line; translated from pseudocode in Objects/lnotab_notes.txt ''' - co_linetable = self.pyop_field('co_linetable').proxyval(set()) + co_locationtable = self.pyop_field('co_locationtable').proxyval(set()) # Initialize lineno to co_firstlineno as per PyCode_Addr2Line # not 0, as lnotab_notes.txt has it: @@ -658,18 +715,9 @@ def addr2line(self, addrq): if addrq < 0: return lineno addr = 0 - for addr_incr, line_incr in zip(co_linetable[::2], co_linetable[1::2]): - if addr_incr == 255: - break - addr += ord(addr_incr) - line_delta = ord(line_incr) - if line_delta == 128: - line_delta = 0 - elif line_delta > 128: - line_delta -= 256 - lineno += line_delta - if addr > addrq: - return lineno + for addr, end_addr, line in parse_location_table(lineno, co_locationtable): + if addr <= addrq and end_addr > addrq: + return line assert False, "Unreachable" diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index dea6eadef1f05e..8a2921884c5fd5 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -237,7 +237,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_filename = self.generate(name + "_filename", code.co_filename) co_name = self.generate(name + "_name", code.co_name) co_qualname = self.generate(name + "_qualname", code.co_qualname) - co_linetable = self.generate(name + "_linetable", code.co_linetable) co_locationtable = self.generate(name + "_locationtable", code.co_locationtable) co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable) # These fields are not directly accessible @@ -276,7 +275,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f".co_filename = {co_filename},") self.write(f".co_name = {co_name},") self.write(f".co_qualname = {co_qualname},") - self.write(f".co_linetable = {co_linetable},") self.write(f".co_locationtable = {co_locationtable},") self.write(f".co_code_adaptive = {co_code_adaptive},") name_as_code = f"(PyCodeObject *)&{name}" diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index f3454e6209e5b7..d2c0b873d0bbd0 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -288,7 +288,6 @@ def R_REF(obj: Any) -> Any: retval.co_name = self.r_object() retval.co_qualname = self.r_object() retval.co_firstlineno = self.r_long() - retval.co_linetable = self.r_object() retval.co_locationtable = self.r_object() retval.co_exceptiontable = self.r_object() return retval From 62228e0681da877dde1bd81225ab8ffdddf0763c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 14 Apr 2022 18:14:51 +0100 Subject: [PATCH 06/18] Remove unused table generation code from compiler. --- Python/compile.c | 193 +---------------------------------------------- 1 file changed, 4 insertions(+), 189 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 5bc5b9bfa21bc3..cec14c198c84e8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7017,12 +7017,7 @@ struct assembler { PyObject *a_bytecode; /* bytes containing bytecode */ int a_offset; /* offset into bytecode */ int a_nblocks; /* number of reachable blocks */ - PyObject *a_lnotab; /* bytes containing lnotab */ - PyObject* a_enotab; /* bytes containing enotab */ - PyObject* a_cnotab; /* bytes containing cnotab */ - int a_lnotab_off; /* offset into lnotab */ - int a_enotab_off; /* offset into enotab */ - int a_cnotab_off; /* offset into cnotab */ + PyObject *a_except_table; /* bytes containing exception table */ int a_except_table_off; /* offset into exception table */ int a_prevlineno; /* lineno of last emitted line in line table */ @@ -7134,31 +7129,15 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) memset(a, 0, sizeof(struct assembler)); a->a_prevlineno = a->a_lineno = firstlineno; a->a_prev_end_lineno = a->a_end_lineno = firstlineno; - a->a_lnotab = NULL; - a->a_enotab = NULL; - a->a_cnotab = NULL; a->a_locationtable = NULL; a->a_location_off = 0; - a->a_cnotab_off = 0; a->a_except_table = NULL; a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); if (a->a_bytecode == NULL) { goto error; } - a->a_lnotab = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); - if (a->a_lnotab == NULL) { - goto error; - } - a->a_enotab = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); - if (a->a_enotab == NULL) { - goto error; - } - a->a_cnotab = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); - if (a->a_cnotab == NULL) { - goto error; - } a->a_locationtable = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); - if (a->a_lnotab == NULL) { + if (a->a_locationtable == NULL) { goto error; } a->a_except_table = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); @@ -7172,9 +7151,6 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) return 1; error: Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_lnotab); - Py_XDECREF(a->a_enotab); - Py_XDECREF(a->a_cnotab); Py_XDECREF(a->a_locationtable); Py_XDECREF(a->a_except_table); return 0; @@ -7184,9 +7160,6 @@ static void assemble_free(struct assembler *a) { Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_lnotab); - Py_XDECREF(a->a_enotab); - Py_XDECREF(a->a_cnotab); Py_XDECREF(a->a_locationtable); Py_XDECREF(a->a_except_table); } @@ -7203,25 +7176,6 @@ blocksize(basicblock *b) return size; } -static int -assemble_emit_table_pair(struct assembler* a, PyObject** table, int* offset, - int left, int right) -{ - Py_ssize_t len = PyBytes_GET_SIZE(*table); - if (*offset + 2 >= len) { - if (_PyBytes_Resize(table, len * 2) < 0) - return 0; - } - unsigned char* table_entry = (unsigned char*)PyBytes_AS_STRING(*table); - - table_entry += *offset; - *offset += 2; - - *table_entry++ = left; - *table_entry++ = right; - return 1; -} - static basicblock * push_except_block(ExceptStack *stack, struct instr *setup) { assert(is_block_push(setup)); @@ -7465,117 +7419,6 @@ assemble_exception_table(struct assembler *a) return 1; } -/* Appends a range to the end of the line number table. See - * Objects/lnotab_notes.txt for the description of the line number table. */ - -static int -assemble_line_range(struct assembler* a, int current, PyObject** table, - int* prev, int* start, int* offset) -{ - int ldelta, bdelta; - bdelta = (a->a_offset - *start) * sizeof(_Py_CODEUNIT); - if (bdelta == 0) { - return 1; - } - if (current < 0) { - ldelta = -128; - } - else { - ldelta = current - *prev; - *prev = current; - while (ldelta > 127) { - if (!assemble_emit_table_pair(a, table, offset, 0, 127)) { - return 0; - } - ldelta -= 127; - } - while (ldelta < -127) { - if (!assemble_emit_table_pair(a, table, offset, 0, -127)) { - return 0; - } - ldelta += 127; - } - } - assert(-128 <= ldelta && ldelta < 128); - while (bdelta > 254) { - if (!assemble_emit_table_pair(a, table, offset, 254, ldelta)) { - return 0; - } - ldelta = current < 0 ? -128 : 0; - bdelta -= 254; - } - if (!assemble_emit_table_pair(a, table, offset, bdelta, ldelta)) { - return 0; - } - *start = a->a_offset; - return 1; -} - -static int -assemble_start_line_range(struct assembler* a) { - return assemble_line_range(a, a->a_lineno, &a->a_lnotab, - &a->a_prevlineno, &a->a_lineno_start, &a->a_lnotab_off); -} - -static int -assemble_end_line_range(struct assembler* a) { - return assemble_line_range(a, a->a_end_lineno, &a->a_enotab, - &a->a_prev_end_lineno, &a->a_end_lineno_start, &a->a_enotab_off); -} - -static int -assemble_lnotab(struct assembler* a, struct instr* i) -{ - if (i->i_lineno == a->a_lineno) { - return 1; - } - if (!assemble_start_line_range(a)) { - return 0; - } - a->a_lineno = i->i_lineno; - return 1; -} - -static int -assemble_enotab(struct assembler* a, struct instr* i) -{ - if (i->i_end_lineno == a->a_end_lineno) { - return 1; - } - if (!assemble_end_line_range(a)) { - return 0; - } - a->a_end_lineno = i->i_end_lineno; - return 1; -} - -static int -assemble_cnotab(struct assembler* a, struct instr* i, int instr_size) -{ - Py_ssize_t len = PyBytes_GET_SIZE(a->a_cnotab); - int difference = instr_size * 2; - if (a->a_cnotab_off + difference >= len) { - if (_PyBytes_Resize(&a->a_cnotab, difference + (len * 2)) < 0) { - return 0; - } - } - - unsigned char* cnotab = (unsigned char*)PyBytes_AS_STRING(a->a_cnotab); - cnotab += a->a_cnotab_off; - a->a_cnotab_off += difference; - - for (int j = 0; j < instr_size; j++) { - if (i->i_col_offset > 255 || i->i_end_col_offset > 255) { - *cnotab++ = 0; - *cnotab++ = 0; - continue; - } - *cnotab++ = i->i_col_offset + 1; - *cnotab++ = i->i_end_col_offset + 1; - } - return 1; -} - /* Code location formats (this looks better if rendered as markdown) Format | First byte | Subsequent bytes | Meaning @@ -7741,15 +7584,6 @@ assemble_emit(struct assembler *a, struct instr *i) _Py_CODEUNIT *code; int size = instr_size(i); - if (i->i_lineno && !assemble_lnotab(a, i)) { - return 0; - } - if (!assemble_enotab(a, i)) { - return 0; - } - if (!assemble_cnotab(a, i, size)) { - return 0; - } if (a->a_offset + size >= len / (int)sizeof(_Py_CODEUNIT)) { if (len > PY_SSIZE_T_MAX / 2) return 0; @@ -8518,33 +8352,14 @@ assemble(struct compiler *c, int addNone) if (!merge_const_one(c, &a.a_except_table)) { goto error; } - if (!assemble_start_line_range(&a)) { - return 0; - } - if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) { - goto error; - } - if (!merge_const_one(c, &a.a_lnotab)) { - goto error; - } - if (!assemble_end_line_range(&a)) { - return 0; - } - if (_PyBytes_Resize(&a.a_enotab, a.a_enotab_off) < 0) { - goto error; - } - if (!merge_const_one(c, &a.a_enotab)) { - goto error; - } - if (_PyBytes_Resize(&a.a_cnotab, a.a_cnotab_off) < 0) { - goto error; - } + if (_PyBytes_Resize(&a.a_locationtable, a.a_location_off) < 0) { goto error; } if (!merge_const_one(c, &a.a_locationtable)) { goto error; } + if (_PyBytes_Resize(&a.a_bytecode, a.a_offset * sizeof(_Py_CODEUNIT)) < 0) { goto error; } From ab8cd4f33daeb9e72b5de28655f8bf5cb876fb7e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 10:11:57 +0100 Subject: [PATCH 07/18] Rename locationtable back to linetable to conform to PEP 626. --- Include/cpython/code.h | 2 +- Include/internal/pycore_code.h | 4 +-- Lib/test/test_code.py | 14 ++++---- Lib/test/test_compile.py | 4 +-- Lib/test/test_dis.py | 4 +-- Lib/test/test_exceptions.py | 2 +- Lib/test/test_marshal.py | 2 +- Objects/clinic/codeobject.c.h | 25 +++++++------- Objects/codeobject.c | 61 +++++++++++++++++----------------- Python/compile.c | 24 ++++++------- Python/marshal.c | 12 +++---- Tools/gdb/libpython.py | 8 ++--- Tools/scripts/deepfreeze.py | 4 +-- Tools/scripts/umarshal.py | 2 +- 14 files changed, 83 insertions(+), 85 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index a8d52133aba874..4ff56d994d872f 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -86,7 +86,7 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_filename; /* unicode (where it was loaded from) */ \ PyObject *co_name; /* unicode (name, for reference) */ \ PyObject *co_qualname; /* unicode (qualname, for reference) */ \ - PyObject *co_locationtable; /* bytes object that holds location info */ \ + PyObject *co_linetable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ /* Scratch space for extra data relating to the code object. \ Type is a void* to keep the format private in codeobject.c to force \ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 630069eb631f8c..5db677a5d6d4ad 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -175,7 +175,7 @@ struct _PyCodeConstructor { /* the code */ PyObject *code; int firstlineno; - PyObject *locationtable; + PyObject *linetable; /* used by the code */ PyObject *consts; @@ -224,7 +224,7 @@ extern int _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds /** Out of process API for initializing the location table. */ extern void _PyLocationTable_InitAddressRange( - const char *locationtable, + const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range); diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index a6972935d406e6..9529a21b53bb03 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -230,7 +230,7 @@ def func(): pass co.co_name, co.co_qualname, co.co_firstlineno, - co.co_locationtable, + co.co_linetable, co.co_exceptiontable, co.co_freevars, co.co_cellvars) @@ -306,7 +306,7 @@ def func(): co.co_name, co.co_qualname, co.co_firstlineno, - co.co_locationtable, + co.co_linetable, co.co_exceptiontable, co.co_freevars, co.co_cellvars, @@ -326,10 +326,10 @@ def func(arg): newcode = code.replace(co_name="func") # Should not raise SystemError self.assertEqual(code, newcode) - def test_empty_locationtable(self): + def test_empty_linetable(self): def func(): pass - new_code = code = func.__code__.replace(co_locationtable=b'') + new_code = code = func.__code__.replace(co_linetable=b'') self.assertEqual(list(new_code.co_lines()), []) @requires_debug_ranges() @@ -415,10 +415,10 @@ def f(): # co_positions behavior when info is missing. @requires_debug_ranges() - def test_co_positions_empty_locationtable(self): + def test_co_positions_empty_linetable(self): def func(): x = 1 - new_code = func.__code__.replace(co_locationtable=b'') + new_code = func.__code__.replace(co_linetable=b'') positions = new_code.co_positions() for line, end_line, column, end_column in positions: self.assertIsNone(line) @@ -524,7 +524,7 @@ def read_signed_varint(it): def parse_location_table(code): line = code.co_firstlineno - it = iter(code.co_locationtable) + it = iter(code.co_linetable) while True: try: first_byte = read(it) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 7ed818e6bbe23a..5a9c618786f4e2 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -643,7 +643,7 @@ def check_same_constant(const): self.check_constant(f1, frozenset({0})) self.assertTrue(f1(0)) - # Merging equal co_locationtable is not a strict requirement + # Merging equal co_linetable is not a strict requirement # for the Python semantics, it's a more an implementation detail. @support.cpython_only def test_merge_code_attrs(self): @@ -651,7 +651,7 @@ def test_merge_code_attrs(self): f1 = lambda x: x.y.z f2 = lambda a: a.b.c - self.assertIs(f1.__code__.co_locationtable, f2.__code__.co_locationtable) + self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable) # Stripping unused constants is not a strict requirement for the # Python semantics, it's a more an implementation detail. diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 09f046c0821c37..0a0cde1482a1c8 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -196,7 +196,7 @@ def bug42562(): # Set line number for 'pass' to None -bug42562.__code__ = bug42562.__code__.replace(co_locationtable=b'\xf8') +bug42562.__code__ = bug42562.__code__.replace(co_linetable=b'\xf8') dis_bug42562 = """\ @@ -1425,7 +1425,7 @@ def test_co_positions(self): @requires_debug_ranges() def test_co_positions_missing_info(self): code = compile('x, y, z', '', 'exec') - code_without_location_table = code.replace(co_locationtable=b'') + code_without_location_table = code.replace(co_linetable=b'') actual = dis.get_instructions(code_without_location_table) for instruction in actual: with self.subTest(instruction=instruction): diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 55a1f419e01836..73f2f275cd8565 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2652,7 +2652,7 @@ def test_missing_lineno_shows_as_none(self): def f(): 1/0 self.lineno_after_raise(f, 1) - f.__code__ = f.__code__.replace(co_locationtable=b'\xf8\xf8\xf8\xf9\xf8\xf8\xf8') + f.__code__ = f.__code__.replace(co_linetable=b'\xf8\xf8\xf8\xf9\xf8\xf8\xf8') self.lineno_after_raise(f, None) def test_lineno_after_raise_in_with_exit(self): diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 959b4e5d93ba66..aae86cc257d7e1 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -129,7 +129,7 @@ def test_different_filenames(self): self.assertEqual(co2.co_filename, "f2") @requires_debug_ranges() - def test_minimal_locationtable_with_no_debug_ranges(self): + def test_minimal_linetable_with_no_debug_ranges(self): # Make sure when demarshalling objects with `-X no_debug_ranges` # that the columns are None. co = ExceptionTestCase.test_exceptions.__code__ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index e844187c1d7035..553266b57fa3ca 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -5,7 +5,7 @@ preserve PyDoc_STRVAR(code_new__doc__, "code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n" " flags, codestring, constants, names, varnames, filename, name,\n" -" qualname, firstlineno, locationtable, exceptiontable, freevars=(),\n" +" qualname, firstlineno, linetable, exceptiontable, freevars=(),\n" " cellvars=(), /)\n" "--\n" "\n" @@ -16,7 +16,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, - PyObject *qualname, int firstlineno, PyObject *locationtable, + PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable, PyObject *freevars, PyObject *cellvars); @@ -38,7 +38,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *name; PyObject *qualname; int firstlineno; - PyObject *locationtable; + PyObject *linetable; PyObject *exceptiontable; PyObject *freevars = NULL; PyObject *cellvars = NULL; @@ -127,7 +127,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) _PyArg_BadArgument("code", "argument 15", "bytes", PyTuple_GET_ITEM(args, 14)); goto exit; } - locationtable = PyTuple_GET_ITEM(args, 14); + linetable = PyTuple_GET_ITEM(args, 14); if (!PyBytes_Check(PyTuple_GET_ITEM(args, 15))) { _PyArg_BadArgument("code", "argument 16", "bytes", PyTuple_GET_ITEM(args, 15)); goto exit; @@ -150,7 +150,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } cellvars = PyTuple_GET_ITEM(args, 17); skip_optional: - return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, locationtable, exceptiontable, freevars, cellvars); + return_value = code_new_impl(type, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, qualname, firstlineno, linetable, exceptiontable, freevars, cellvars); exit: return return_value; @@ -162,7 +162,7 @@ PyDoc_STRVAR(code_replace__doc__, " co_flags=-1, co_firstlineno=-1, co_code=None, co_consts=None,\n" " co_names=None, co_varnames=None, co_freevars=None,\n" " co_cellvars=None, co_filename=None, co_name=None,\n" -" co_qualname=None, co_locationtable=None, co_exceptiontable=None)\n" +" co_qualname=None, co_linetable=None, co_exceptiontable=None)\n" "--\n" "\n" "Return a copy of the code object with new values for the specified fields."); @@ -179,14 +179,13 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyObject *co_locationtable, - PyBytesObject *co_exceptiontable); + PyObject *co_linetable, PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_locationtable", "co_exceptiontable", NULL}; + static const char * const _keywords[] = {"co_argcount", "co_posonlyargcount", "co_kwonlyargcount", "co_nlocals", "co_stacksize", "co_flags", "co_firstlineno", "co_code", "co_consts", "co_names", "co_varnames", "co_freevars", "co_cellvars", "co_filename", "co_name", "co_qualname", "co_linetable", "co_exceptiontable", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "replace", 0}; PyObject *argsbuf[18]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; @@ -206,7 +205,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_filename = self->co_filename; PyObject *co_name = self->co_name; PyObject *co_qualname = self->co_qualname; - PyObject *co_locationtable = self->co_locationtable; + PyObject *co_linetable = self->co_linetable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); @@ -379,7 +378,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } } if (args[16]) { - co_locationtable = args[16]; + co_linetable = args[16]; if (!--noptargs) { goto skip_optional_kwonly; } @@ -390,7 +389,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } co_exceptiontable = (PyBytesObject *)args[17]; skip_optional_kwonly: - return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_locationtable, co_exceptiontable); + return_value = code_replace_impl(self, co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_firstlineno, co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_linetable, co_exceptiontable); exit: return return_value; @@ -432,4 +431,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=82434fea629ecd68 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9bad1ea605d07309 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 781ec439c60980..09051a0609872c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -243,7 +243,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) con->name == NULL || !PyUnicode_Check(con->name) || con->qualname == NULL || !PyUnicode_Check(con->qualname) || con->filename == NULL || !PyUnicode_Check(con->filename) || - con->locationtable == NULL || !PyBytes_Check(con->locationtable) || + con->linetable == NULL || !PyBytes_Check(con->linetable) || con->exceptiontable == NULL || !PyBytes_Check(con->exceptiontable) ) { PyErr_BadInternalCall(); @@ -301,8 +301,8 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_flags = con->flags; co->co_firstlineno = con->firstlineno; - Py_INCREF(con->locationtable); - co->co_locationtable = con->locationtable; + Py_INCREF(con->linetable); + co->co_linetable = con->linetable; Py_INCREF(con->consts); co->co_consts = con->consts; @@ -478,14 +478,14 @@ _PyCode_New(struct _PyCodeConstructor *con) } PyObject *replacement_locations = NULL; - // Compact the locationtable if we are opted out of debug + // Compact the linetable if we are opted out of debug // ranges. if (!_Py_GetConfig()->code_debug_ranges) { - replacement_locations = remove_column_info(con->locationtable); + replacement_locations = remove_column_info(con->linetable); if (replacement_locations == NULL) { return NULL; } - con->locationtable = replacement_locations; + con->linetable = replacement_locations; } Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); @@ -511,7 +511,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, - PyObject *locationtable, + PyObject *linetable, PyObject *exceptiontable) { PyCodeObject *co = NULL; @@ -589,7 +589,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, .code = code, .firstlineno = firstlineno, - .locationtable = locationtable, + .linetable = linetable, .consts = consts, .names = names, @@ -635,14 +635,14 @@ PyCode_New(int argcount, int kwonlyargcount, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, - PyObject *locationtable, + PyObject *linetable, PyObject *exceptiontable) { return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, - locationtable, + linetable, exceptiontable); } @@ -674,7 +674,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .qualname = funcname_ob, .code = emptystring, .firstlineno = firstlineno, - .locationtable = emptystring, + .linetable = emptystring, .consts = nulltuple, .names = nulltuple, .localsplusnames = nulltuple, @@ -695,7 +695,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) * source location tracking (co_lines/co_positions) ******************/ -/* Use co_locationtable to compute the line number from a bytecode index, addrq. See +/* Use co_linetable to compute the line number from a bytecode index, addrq. See lnotab_notes.txt for the details of the lnotab representation. */ @@ -712,9 +712,9 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) } void -_PyLocationTable_InitAddressRange(const char *locationtable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) +_PyLocationTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) { - range->opaque.lo_next = (const uint8_t *)locationtable; + range->opaque.lo_next = (const uint8_t *)linetable; range->opaque.limit = range->opaque.lo_next + length; range->ar_start = -1; range->ar_end = 0; @@ -725,10 +725,10 @@ _PyLocationTable_InitAddressRange(const char *locationtable, Py_ssize_t length, int _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) { - assert(co->co_locationtable != NULL); - const char *locationtable = PyBytes_AS_STRING(co->co_locationtable); - Py_ssize_t length = PyBytes_GET_SIZE(co->co_locationtable); - _PyLocationTable_InitAddressRange(locationtable, length, co->co_firstlineno, bounds); + assert(co->co_linetable != NULL); + const char *linetable = PyBytes_AS_STRING(co->co_linetable); + Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); + _PyLocationTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); return bounds->ar_line; } @@ -1405,7 +1405,7 @@ code.__new__ as code_new name: unicode qualname: unicode firstlineno: int - locationtable: object(subclass_of="&PyBytes_Type") + linetable: object(subclass_of="&PyBytes_Type") exceptiontable: object(subclass_of="&PyBytes_Type") freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = () @@ -1419,10 +1419,10 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *filename, PyObject *name, - PyObject *qualname, int firstlineno, PyObject *locationtable, + PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable, PyObject *freevars, PyObject *cellvars) -/*[clinic end generated code: output=59e466c5f94e3983 input=f1a94ca09a42b875]*/ +/*[clinic end generated code: output=069fa20d299f9dda input=e31da3c41ad8064a]*/ { PyObject *co = NULL; PyObject *ournames = NULL; @@ -1489,7 +1489,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount, ourvarnames, ourfreevars, ourcellvars, filename, name, qualname, firstlineno, - locationtable, + linetable, exceptiontable ); cleanup: @@ -1525,7 +1525,7 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_filename); Py_XDECREF(co->co_name); Py_XDECREF(co->co_qualname); - Py_XDECREF(co->co_locationtable); + Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_exceptiontable); if (co->co_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject*)co); @@ -1674,7 +1674,7 @@ static PyMemberDef code_memberlist[] = { {"co_name", T_OBJECT, OFF(co_name), READONLY}, {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_locationtable", T_OBJECT, OFF(co_locationtable), READONLY}, + {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ }; @@ -1769,7 +1769,7 @@ code.replace co_filename: unicode(c_default="self->co_filename") = None co_name: unicode(c_default="self->co_name") = None co_qualname: unicode(c_default="self->co_qualname") = None - co_locationtable: object(c_default="self->co_locationtable") = None + co_linetable: object(c_default="self->co_linetable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None Return a copy of the code object with new values for the specified fields. @@ -1784,9 +1784,8 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyObject *co_locationtable, - PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=7e046bf6eabcd5b2 input=3ba7aa09c21e4be9]*/ + PyObject *co_linetable, PyBytesObject *co_exceptiontable) +/*[clinic end generated code: output=d545430b5261cbf4 input=d4c5675d7a781ba9]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -1847,9 +1846,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_freevars = freevars; } - if (!Py_IsNone(co_locationtable) && !PyBytes_Check(co_locationtable)) { + if (!Py_IsNone(co_linetable) && !PyBytes_Check(co_linetable)) { PyErr_SetString(PyExc_ValueError, - "co_locationtable must be None or bytes"); + "co_linetable must be None or bytes"); goto error; } @@ -1858,7 +1857,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, co_qualname, co_firstlineno, - (PyObject*)co_locationtable, (PyObject*)co_exceptiontable); + (PyObject*)co_linetable, (PyObject*)co_exceptiontable); error: Py_XDECREF(code); diff --git a/Python/compile.c b/Python/compile.c index cec14c198c84e8..43cca70181154a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7028,7 +7028,7 @@ struct assembler { int a_end_lineno_start; /* bytecode start offset of current end_lineno */ /* Location Info */ - PyObject* a_locationtable; /* bytes containing location info */ + PyObject* a_linetable; /* bytes containing location info */ int a_location_off; /* offset of last written location info frame */ basicblock *a_entry; @@ -7129,15 +7129,15 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) memset(a, 0, sizeof(struct assembler)); a->a_prevlineno = a->a_lineno = firstlineno; a->a_prev_end_lineno = a->a_end_lineno = firstlineno; - a->a_locationtable = NULL; + a->a_linetable = NULL; a->a_location_off = 0; a->a_except_table = NULL; a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); if (a->a_bytecode == NULL) { goto error; } - a->a_locationtable = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); - if (a->a_locationtable == NULL) { + a->a_linetable = PyBytes_FromStringAndSize(NULL, DEFAULT_CNOTAB_SIZE); + if (a->a_linetable == NULL) { goto error; } a->a_except_table = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); @@ -7151,7 +7151,7 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno) return 1; error: Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_locationtable); + Py_XDECREF(a->a_linetable); Py_XDECREF(a->a_except_table); return 0; } @@ -7160,7 +7160,7 @@ static void assemble_free(struct assembler *a) { Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_locationtable); + Py_XDECREF(a->a_linetable); Py_XDECREF(a->a_except_table); } @@ -7436,7 +7436,7 @@ No column | 11101bbb | static void write_location_byte(struct assembler* a, int val) { - PyBytes_AS_STRING(a->a_locationtable)[a->a_location_off] = val&255; + PyBytes_AS_STRING(a->a_linetable)[a->a_location_off] = val&255; a->a_location_off++; } @@ -7549,10 +7549,10 @@ write_location_info_entry(struct assembler* a, struct instr* i, int isize) static int assemble_emit_location(struct assembler* a, struct instr* i) { - Py_ssize_t len = PyBytes_GET_SIZE(a->a_locationtable); + Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { assert(len > THEORETICAL_MAX_ENTRY_SIZE); - if (_PyBytes_Resize(&a->a_locationtable, len*2) < 0) { + if (_PyBytes_Resize(&a->a_linetable, len*2) < 0) { return 0; } } @@ -7900,7 +7900,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist, .code = a->a_bytecode, .firstlineno = c->u->u_firstlineno, - .locationtable = a->a_locationtable, + .linetable = a->a_linetable, .consts = consts, .names = names, @@ -8353,10 +8353,10 @@ assemble(struct compiler *c, int addNone) goto error; } - if (_PyBytes_Resize(&a.a_locationtable, a.a_location_off) < 0) { + if (_PyBytes_Resize(&a.a_linetable, a.a_location_off) < 0) { goto error; } - if (!merge_const_one(c, &a.a_locationtable)) { + if (!merge_const_one(c, &a.a_linetable)) { goto error; } diff --git a/Python/marshal.c b/Python/marshal.c index 612a9595f13649..bbe67e3379fd93 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -563,7 +563,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(co->co_name, p); w_object(co->co_qualname, p); w_long(co->co_firstlineno, p); - w_object(co->co_locationtable, p); + w_object(co->co_linetable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); } @@ -1355,7 +1355,7 @@ r_object(RFILE *p) PyObject *name = NULL; PyObject *qualname = NULL; int firstlineno; - PyObject* locationtable = NULL; + PyObject* linetable = NULL; PyObject *exceptiontable = NULL; idx = r_ref_reserve(flag, p); @@ -1408,8 +1408,8 @@ r_object(RFILE *p) firstlineno = (int)r_long(p); if (firstlineno == -1 && PyErr_Occurred()) break; - locationtable = r_object(p); - if (locationtable == NULL) + linetable = r_object(p); + if (linetable == NULL) goto code_error; exceptiontable = r_object(p); if (exceptiontable == NULL) @@ -1423,7 +1423,7 @@ r_object(RFILE *p) .code = code, .firstlineno = firstlineno, - .locationtable = locationtable, + .linetable = linetable, .consts = consts, .names = names, @@ -1460,7 +1460,7 @@ r_object(RFILE *p) Py_XDECREF(filename); Py_XDECREF(name); Py_XDECREF(qualname); - Py_XDECREF(locationtable); + Py_XDECREF(linetable); Py_XDECREF(exceptiontable); } retval = v; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 8d0a7034ebfd4d..125aba46ee70ae 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -655,10 +655,10 @@ def read_signed_varint(it): else: return uval >> 1 -def parse_location_table(firstlineno, locationtable): +def parse_location_table(firstlineno, linetable): line = firstlineno addr = 0 - it = iter(locationtable) + it = iter(linetable) while True: try: first_byte = read(it) @@ -706,7 +706,7 @@ def addr2line(self, addrq): Analogous to PyCode_Addr2Line; translated from pseudocode in Objects/lnotab_notes.txt ''' - co_locationtable = self.pyop_field('co_locationtable').proxyval(set()) + co_linetable = self.pyop_field('co_linetable').proxyval(set()) # Initialize lineno to co_firstlineno as per PyCode_Addr2Line # not 0, as lnotab_notes.txt has it: @@ -715,7 +715,7 @@ def addr2line(self, addrq): if addrq < 0: return lineno addr = 0 - for addr, end_addr, line in parse_location_table(lineno, co_locationtable): + for addr, end_addr, line in parse_location_table(lineno, co_linetable): if addr <= addrq and end_addr > addrq: return line assert False, "Unreachable" diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py index 8a2921884c5fd5..8449098db3d6f8 100644 --- a/Tools/scripts/deepfreeze.py +++ b/Tools/scripts/deepfreeze.py @@ -237,7 +237,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_filename = self.generate(name + "_filename", code.co_filename) co_name = self.generate(name + "_name", code.co_name) co_qualname = self.generate(name + "_qualname", code.co_qualname) - co_locationtable = self.generate(name + "_locationtable", code.co_locationtable) + co_linetable = self.generate(name + "_linetable", code.co_linetable) co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable) # These fields are not directly accessible localsplusnames, localspluskinds = get_localsplus(code) @@ -275,7 +275,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.write(f".co_filename = {co_filename},") self.write(f".co_name = {co_name},") self.write(f".co_qualname = {co_qualname},") - self.write(f".co_locationtable = {co_locationtable},") + self.write(f".co_linetable = {co_linetable},") self.write(f".co_code_adaptive = {co_code_adaptive},") name_as_code = f"(PyCodeObject *)&{name}" self.deallocs.append(f"_PyStaticCode_Dealloc({name_as_code});") diff --git a/Tools/scripts/umarshal.py b/Tools/scripts/umarshal.py index d2c0b873d0bbd0..f61570cbaff751 100644 --- a/Tools/scripts/umarshal.py +++ b/Tools/scripts/umarshal.py @@ -288,7 +288,7 @@ def R_REF(obj: Any) -> Any: retval.co_name = self.r_object() retval.co_qualname = self.r_object() retval.co_firstlineno = self.r_long() - retval.co_locationtable = self.r_object() + retval.co_linetable = self.r_object() retval.co_exceptiontable = self.r_object() return retval elif type == Type.REF: From 4e1584ae034382c5c5df337388b2367481edab02 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 12:24:59 +0100 Subject: [PATCH 08/18] Use enum values and fix gdb support. --- Include/cpython/code.h | 19 +++++---- Include/internal/pycore_code.h | 2 +- Lib/test/test_code.py | 7 +-- Objects/codeobject.c | 51 +++++++++++----------- Python/compile.c | 78 +++++++++++++++------------------- Tools/gdb/libpython.py | 8 ++-- 6 files changed, 78 insertions(+), 87 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 4ff56d994d872f..be3b10bba724b8 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -86,7 +86,7 @@ typedef uint16_t _Py_CODEUNIT; PyObject *co_filename; /* unicode (where it was loaded from) */ \ PyObject *co_name; /* unicode (name, for reference) */ \ PyObject *co_qualname; /* unicode (qualname, for reference) */ \ - PyObject *co_linetable; /* bytes object that holds location info */ \ + PyObject *co_linetable; /* bytes object that holds location info */ \ PyObject *co_weakreflist; /* to support weakrefs to code objects */ \ /* Scratch space for extra data relating to the code object. \ Type is a void* to keep the format private in codeobject.c to force \ @@ -204,15 +204,16 @@ PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, typedef enum _PyCodeLocationInfoKind { + /* short forms are 0 to 9 */ PY_CODE_LOCATION_INFO_SHORT0 = 0, - PY_CODE_LOCATION_INFO_SHORT1 = 1, - PY_CODE_LOCATION_INFO_SHORT2 = 2, - PY_CODE_LOCATION_INFO_SHORT3 = 3, - PY_CODE_LOCATION_INFO_SHORT4 = 4, - PY_CODE_LOCATION_INFO_SHORT5 = 5, - - PYCODE_LOCATION_INFO_TWO_LINES = 14, - PYCODE_LOCATION_INFO_NONE = 15 + /* one lineforms are 10 to 12 */ + PY_CODE_LOCATION_INFO_ONE_LINE0 = 10, + PY_CODE_LOCATION_INFO_ONE_LINE1 = 11, + PY_CODE_LOCATION_INFO_ONE_LINE2 = 12, + + PY_CODE_LOCATION_INFO_NO_COLUMNS = 13, + PY_CODE_LOCATION_INFO_LONG = 14, + PY_CODE_LOCATION_INFO_NONE = 15 } _PyCodeLocationInfoKind; #ifdef __cplusplus diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 5db677a5d6d4ad..66dc447b7e7289 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -223,7 +223,7 @@ extern PyObject* _PyCode_GetCode(PyCodeObject *); extern int _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds); /** Out of process API for initializing the location table. */ -extern void _PyLocationTable_InitAddressRange( +extern void _PyLineTable_InitAddressRange( const char *linetable, Py_ssize_t length, int firstlineno, diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 9529a21b53bb03..a37ebd27dc3882 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -270,6 +270,7 @@ def func2(): ("co_cellvars", ("cellvar",)), ("co_filename", "newfilename"), ("co_name", "newname"), + ("co_linetable", code2.co_linetable), ): with self.subTest(attr=attr, value=value): new_code = code.replace(**{attr: value}) @@ -376,9 +377,9 @@ def test_co_positions_artificial_instructions(self): ("LOAD_CONST", None), # artificial 'None' ("STORE_NAME", "e"), # XX: we know the location for this ("DELETE_NAME", "e"), - ('RERAISE', 1), - ('COPY', 3), - ('POP_EXCEPT', None), + ("RERAISE", 1), + ("COPY", 3), + ("POP_EXCEPT", None), ("RERAISE", 1) ] ) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 09051a0609872c..f66b1c48107eb8 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -370,16 +370,17 @@ get_line_delta(const uint8_t *ptr) { int code = ((*ptr) >> 3) & 15; switch (code) { - case 15: + case PY_CODE_LOCATION_INFO_NONE: return 0; - case 13: /* No column */ - case 14: /* Long form */ + case PY_CODE_LOCATION_INFO_NO_COLUMNS: + case PY_CODE_LOCATION_INFO_LONG: return scan_signed_varint(ptr+1); - case 10: - case 11: - case 12: - /* One line forms */ - return code - 10; + case PY_CODE_LOCATION_INFO_ONE_LINE0: + return 0; + case PY_CODE_LOCATION_INFO_ONE_LINE1: + return 1; + case PY_CODE_LOCATION_INFO_ONE_LINE2: + return 2; default: /* Same line */ return 0; @@ -861,14 +862,12 @@ advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, in bounds->ar_start = bounds->ar_end; bounds->ar_end = bounds->ar_start + ((first_byte & 7) + 1) * sizeof(_Py_CODEUNIT); switch(code) { - case 15: - /* None */ + case PY_CODE_LOCATION_INFO_NONE: bounds->ar_line = *endline = -1; *column = *endcolumn = -1; break; - case 14: + case PY_CODE_LOCATION_INFO_LONG: { - /* Long form */ bounds->opaque.computed_line += read_signed_varint(bounds); bounds->ar_line = bounds->opaque.computed_line; *endline = bounds->ar_line + read_varint(bounds); @@ -876,7 +875,7 @@ advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, in *endcolumn = read_varint(bounds)-1; break; } - case 13: + case PY_CODE_LOCATION_INFO_NO_COLUMNS: { /* No column */ bounds->opaque.computed_line += read_signed_varint(bounds); @@ -884,9 +883,9 @@ advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, in *column = *endcolumn = -1; break; } - case 10: - case 11: - case 12: + case PY_CODE_LOCATION_INFO_ONE_LINE0: + case PY_CODE_LOCATION_INFO_ONE_LINE1: + case PY_CODE_LOCATION_INFO_ONE_LINE2: { /* one line form */ int line_delta = code - 10; @@ -896,9 +895,9 @@ advance_with_locations(PyCodeAddressRange *bounds, int *endline, int *column, in *endcolumn = read_byte(bounds); break; } - default: /* 0 to 9 */ + default: { - /* Short form */ + /* Short forms */ int second_byte = read_byte(bounds); assert((second_byte & 128) == 0); *endline = bounds->ar_line = bounds->opaque.computed_line; @@ -967,15 +966,15 @@ _PyLineTable_StartsLine(PyCodeAddressRange *range) } while (((*ptr) & 128) == 0); int code = ((*ptr)>> 3) & 15; switch(code) { - case 15: + case PY_CODE_LOCATION_INFO_LONG: return 0; - case 13: - case 14: + case PY_CODE_LOCATION_INFO_NO_COLUMNS: + case PY_CODE_LOCATION_INFO_NONE: return ptr[1] != 0; - case 10: + case PY_CODE_LOCATION_INFO_ONE_LINE0: return 0; - case 11: - case 12: + case PY_CODE_LOCATION_INFO_ONE_LINE1: + case PY_CODE_LOCATION_INFO_ONE_LINE2: return 1; default: return 0; @@ -1674,7 +1673,7 @@ static PyMemberDef code_memberlist[] = { {"co_name", T_OBJECT, OFF(co_name), READONLY}, {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, + {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, {NULL} /* Sentinel */ }; @@ -1769,7 +1768,7 @@ code.replace co_filename: unicode(c_default="self->co_filename") = None co_name: unicode(c_default="self->co_name") = None co_qualname: unicode(c_default="self->co_qualname") = None - co_linetable: object(c_default="self->co_linetable") = None + co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None co_exceptiontable: PyBytesObject(c_default="(PyBytesObject *)self->co_exceptiontable") = None Return a copy of the code object with new values for the specified fields. diff --git a/Python/compile.c b/Python/compile.c index 43cca70181154a..f5f40fcd10909b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7017,7 +7017,6 @@ struct assembler { PyObject *a_bytecode; /* bytes containing bytecode */ int a_offset; /* offset into bytecode */ int a_nblocks; /* number of reachable blocks */ - PyObject *a_except_table; /* bytes containing exception table */ int a_except_table_off; /* offset into exception table */ int a_prevlineno; /* lineno of last emitted line in line table */ @@ -7026,12 +7025,10 @@ struct assembler { int a_end_lineno; /* end_lineno of last emitted instruction */ int a_lineno_start; /* bytecode start offset of current lineno */ int a_end_lineno_start; /* bytecode start offset of current end_lineno */ - + basicblock *a_entry; /* Location Info */ PyObject* a_linetable; /* bytes containing location info */ int a_location_off; /* offset of last written location info frame */ - - basicblock *a_entry; }; Py_LOCAL_INLINE(void) @@ -7419,19 +7416,9 @@ assemble_exception_table(struct assembler *a) return 1; } -/* Code location formats (this looks better if rendered as markdown) - -Format | First byte | Subsequent bytes | Meaning ------- | ------- | ------- | ------- -Short | 1nnnnbbb | 0ccceeee | Same line as previous entry. Column = `nnnn`*8 + `ccc`, end column = column + `eeee`. `n` < 10 -Indent | 11xxxbbb | 0ccceeee | One line. Line = previous + xxx-1. Column = `ccc`*4, end column = column + `eeee`. 2 <= x < 5 -Long form | 11110bbb | signed varint `l`, varint `e`, unsigned varint `c` ,unsigned varint `x` | Line = previous + `l`, end_line = line + `e`, column = c, end_column = x -None | 11111bbb | --- | No location info - -No column | 11101bbb | - -*/ +/* Code location emitting code. See locations.md for a description of the format. */ +#define MSB 0x80 static void write_location_byte(struct assembler* a, int val) @@ -7440,6 +7427,14 @@ write_location_byte(struct assembler* a, int val) a->a_location_off++; } + +static void +write_location_first_byte(struct assembler* a, int code, int length) +{ + assert((code & 15) == code); + write_location_byte(a, MSB | (code << 3) | (length - 1)); +} + static void write_location_varint(struct assembler* a, unsigned int val) { @@ -7467,10 +7462,10 @@ write_location_info_short_form(struct assembler* a, int length, int column, int { assert(length > 0 && length <= 8); int column_low_bits = column & 7; - int column_high_bits = column & 0x78; + int column_group = column >> 3; assert(column < 80); assert(end_column - column < 16); - write_location_byte(a, 0x80 | column_high_bits | (length - 1)); + write_location_first_byte(a, PY_CODE_LOCATION_INFO_SHORT0 + column_group, length); write_location_byte(a, (column_low_bits << 4) | (end_column - column)); } @@ -7481,7 +7476,7 @@ write_location_info_oneline_form(struct assembler* a, int length, int line_delta assert(line_delta >= 0 && line_delta < 3); assert(column < 128); assert(end_column < 128); - write_location_byte(a, 0x80 | (line_delta + 10) << 3 | (length - 1)); + write_location_first_byte(a, PY_CODE_LOCATION_INFO_ONE_LINE0 + line_delta, length); write_location_byte(a, column); write_location_byte(a, end_column); } @@ -7490,7 +7485,7 @@ static void write_location_info_long_form(struct assembler* a, struct instr* i, int length) { assert(length > 0 && length <= 8); - write_location_byte(a, 0xf0 | (length - 1)); + write_location_first_byte(a, PY_CODE_LOCATION_INFO_LONG, length); write_location_signed_varint(a, i->i_lineno - a->a_lineno); write_location_varint(a, i->i_end_lineno - i->i_lineno); write_location_varint(a, i->i_col_offset+1); @@ -7500,24 +7495,31 @@ write_location_info_long_form(struct assembler* a, struct instr* i, int length) static void write_location_info_none(struct assembler* a, int length) { - write_location_byte(a, 0xf8 | (length-1)); + write_location_first_byte(a, PY_CODE_LOCATION_INFO_NONE, length); } static void write_location_info_no_column(struct assembler* a, int length, int line_delta) { - write_location_byte(a, 0xe8 | (length-1)); + write_location_first_byte(a, PY_CODE_LOCATION_INFO_NO_COLUMNS, length); write_location_signed_varint(a, line_delta); } #define THEORETICAL_MAX_ENTRY_SIZE 25 /* 1 + 6 + 6 + 6 + 6 */ -static void +static int write_location_info_entry(struct assembler* a, struct instr* i, int isize) { + Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); + if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { + assert(len > THEORETICAL_MAX_ENTRY_SIZE); + if (_PyBytes_Resize(&a->a_linetable, len*2) < 0) { + return 0; + } + } if (i->i_lineno < 0) { write_location_info_none(a, isize); - return; + return 1; } int line_delta = i->i_lineno - a->a_lineno; int column = i->i_col_offset; @@ -7528,48 +7530,36 @@ write_location_info_entry(struct assembler* a, struct instr* i, int isize) if (i->i_end_lineno == i->i_lineno) { write_location_info_no_column(a, isize, line_delta); a->a_lineno = i->i_lineno; - return; + return 1; } } else if (i->i_end_lineno == i->i_lineno) { if (line_delta == 0 && column < 80 && end_column - column < 16) { write_location_info_short_form(a, isize, column, end_column); - return; + return 1; } if (line_delta >= 0 && line_delta < 3 && column < 128 && end_column < 128) { write_location_info_oneline_form(a, isize, line_delta, column, end_column); a->a_lineno = i->i_lineno; - return; + return 1; } } write_location_info_long_form(a, i, isize); a->a_lineno = i->i_lineno; + return 1; } static int assemble_emit_location(struct assembler* a, struct instr* i) { - Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); - if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { - assert(len > THEORETICAL_MAX_ENTRY_SIZE); - if (_PyBytes_Resize(&a->a_linetable, len*2) < 0) { + int isize = instr_size(i); + while (isize > 8) { + if (!write_location_info_entry(a, i, 8)) { return 0; } - } - int isize = instr_size(i); - if (isize > 8) { - write_location_info_entry(a, i, 8); isize -= 8; - while (isize > 8) { - write_location_info_none(a, 8); - isize -= 8; - } - write_location_info_none(a, isize); } - else { - write_location_info_entry(a, i, isize); - } - return 1; + return write_location_info_entry(a, i, isize); } /* assemble_emit() diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 125aba46ee70ae..b2c9e8c37dc593 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -636,7 +636,7 @@ def proxyval(self, visited): # Python implementation of location table parsing algorithm def read(it): - return next(it) + return ord(next(it)) def read_varint(it): b = read(it) @@ -671,7 +671,7 @@ def parse_location_table(firstlineno, linetable): yield addr, end_addr, None addr = end_addr continue - elif code == 14: + elif code == 14: # Long form line_delta = read_signed_varint(it) line += line_delta end_line = line + read_varint(it) @@ -1127,8 +1127,8 @@ def current_line_num(self): if self.is_optimized_out(): return None try: - return self.co.addr2line(self.f_lasti*2) - except Exception: + return self.co.addr2line(self.f_lasti) + except Exception as ex: # bpo-34989: addr2line() is a complex function, it can fail in many # ways. For example, it fails with a TypeError on "FakeRepr" if # gdb fails to load debug symbols. Use a catch-all "except From 9783d10abf2c57b83b995afedaa3c3c8d1355431 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 14:41:18 +0100 Subject: [PATCH 09/18] Document location table format --- Objects/locations.md | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Objects/locations.md diff --git a/Objects/locations.md b/Objects/locations.md new file mode 100644 index 00000000000000..18a338a95978a9 --- /dev/null +++ b/Objects/locations.md @@ -0,0 +1,69 @@ +# Locations table + +For versions up to 3.10 see ./lnotab_notes.txt + +In version 3.11 the `co_linetable` bytes object of code objects contains a compact representation of the positions returned by the `co_positions()` iterator. + +The `co_linetable` consists of a sequence of location entries. +Each entry starts with a byte with the most significant bit set, followed by zero or more bytes with most significant bit unset. + +Each entry contains the following information: +* The number of code units covered by this entry (length) +* The start line +* The end line +* The start column +* The end column + +The first byte has the following format: + +Bit 7 | Bits 3-6 | Bits 0-2 + ---- | ---- | ---- + 1 | Code | Length (in code units) - 1 + +The codes are enumerated in the `_PyCodeLocationInfoKind` enum. + +## Variable length integer encodings + +Integers are often encoded using a variable length integer encoding + +### Unsigned integers (varint) + +Unsigned integers are encoded in 6 bit chunks, least significant first. +Each chunk but the last has bit 6 set. +For example: + +* 63 is encoded as `0x3f` +* 200 is encoded as `0x48`, `0x03` + +### Signed integers (svarint) + +Signed integers are encoded by converting them to unsigned integers, using the following function: +```Python +def convert(s): + if s < 0: + return ((-s)<<1) | 1 + else: + return (s<<1) +``` + +## Location entries + +The meaning of the codes and the following bytes are as follows: + +Code | Meaning | Start line | End line | Start column | End column + ---- | ---- | ---- | ---- | ---- | ---- + 0-9 | Short form | Δ 0 | Δ 0 | See below | See below + 10-12 | One line form | Δ (code - 10) | Δ 0 | unsigned byte | unsigned byte + 13 | No column info | Δ svarint | Δ 0 | None | None + 14 | Long form | Δ svarint | Δ varint | varint | varint + 15 | No location | None | None | None | None + +The Δ means the value is encoded as a delta from another value: +* Start line: Delta from the previous start line, or `co_firstlineno` for the first entry. +* End line: Delta from the start line + +### The short forms + +Codes 0-9 are the short forms. The short form consists of two bytes, the second byte holding additional column information. The code is the start column divided by 8 (and rounded down). +* Start column: `(code*8) + ((second_byte>>4)&7)` +* End column: `start_column + (second_byte&15)` From 3020b154d248fb04f60fa73df3921a2cd3bf361d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 15:03:45 +0100 Subject: [PATCH 10/18] Fix potential memory leak --- Objects/codeobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index f66b1c48107eb8..eab0524269e85a 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -492,6 +492,7 @@ _PyCode_New(struct _PyCodeConstructor *con) Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT); PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size); if (co == NULL) { + Py_XDECREF(replacement_locations); PyErr_NoMemory(); return NULL; } From b25595b4bab0db11a72e1ca721b46512e2672a0a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 15:06:25 +0100 Subject: [PATCH 11/18] Restore old name for internal API. --- Objects/codeobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index eab0524269e85a..8db31bc4383104 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -714,7 +714,7 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) } void -_PyLocationTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) +_PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) { range->opaque.lo_next = (const uint8_t *)linetable; range->opaque.limit = range->opaque.lo_next + length; @@ -730,7 +730,7 @@ _PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) assert(co->co_linetable != NULL); const char *linetable = PyBytes_AS_STRING(co->co_linetable); Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); - _PyLocationTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); + _PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); return bounds->ar_line; } From 1a2b359df32db2f7188133661a2517959f6e6bad Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 15:20:08 +0100 Subject: [PATCH 12/18] Re-run argument clinic --- Objects/clinic/codeobject.c.h | 13 +++++++++---- Objects/codeobject.c | 5 +++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index 553266b57fa3ca..41c5c2e1170f8d 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -179,7 +179,8 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyObject *co_linetable, PyBytesObject *co_exceptiontable); + PyBytesObject *co_linetable, + PyBytesObject *co_exceptiontable); static PyObject * code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -205,7 +206,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje PyObject *co_filename = self->co_filename; PyObject *co_name = self->co_name; PyObject *co_qualname = self->co_qualname; - PyObject *co_linetable = self->co_linetable; + PyBytesObject *co_linetable = (PyBytesObject *)self->co_linetable; PyBytesObject *co_exceptiontable = (PyBytesObject *)self->co_exceptiontable; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); @@ -378,7 +379,11 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje } } if (args[16]) { - co_linetable = args[16]; + if (!PyBytes_Check(args[16])) { + _PyArg_BadArgument("replace", "argument 'co_linetable'", "bytes", args[16]); + goto exit; + } + co_linetable = (PyBytesObject *)args[16]; if (!--noptargs) { goto skip_optional_kwonly; } @@ -431,4 +436,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=9bad1ea605d07309 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ebfeec29d2cff674 input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 8db31bc4383104..32f0afa0217163 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1784,8 +1784,9 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_varnames, PyObject *co_freevars, PyObject *co_cellvars, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, - PyObject *co_linetable, PyBytesObject *co_exceptiontable) -/*[clinic end generated code: output=d545430b5261cbf4 input=d4c5675d7a781ba9]*/ + PyBytesObject *co_linetable, + PyBytesObject *co_exceptiontable) +/*[clinic end generated code: output=b6cd9988391d5711 input=f6f68e03571f8d7c]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ From 70e139fa7cb693f52ead71c57907029b92ac444e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 18 Apr 2022 15:23:05 +0100 Subject: [PATCH 13/18] Add NEWS item --- .../Core and Builtins/2022-04-18-15-22-56.bpo-43950.qrTvWL.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-04-18-15-22-56.bpo-43950.qrTvWL.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-18-15-22-56.bpo-43950.qrTvWL.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-18-15-22-56.bpo-43950.qrTvWL.rst new file mode 100644 index 00000000000000..c8bfa5914b0276 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-18-15-22-56.bpo-43950.qrTvWL.rst @@ -0,0 +1,2 @@ +Use a single compact table for line starts, ends and column offsets. Reduces +memory consumption for location info by half From baf38d857fc2da54731ec0062b3bca7a36ad1b16 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 19 Apr 2022 10:01:17 +0100 Subject: [PATCH 14/18] Remove unnecessary check --- Objects/codeobject.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 32f0afa0217163..1899e721d1cdd2 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1847,12 +1847,6 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_freevars = freevars; } - if (!Py_IsNone(co_linetable) && !PyBytes_Check(co_linetable)) { - PyErr_SetString(PyExc_ValueError, - "co_linetable must be None or bytes"); - goto error; - } - co = PyCode_NewWithPosOnlyArgs( co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, From bcb3309f1e01d4a350fd8a1527db31b68de9f157 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 19 Apr 2022 11:21:06 +0100 Subject: [PATCH 15/18] Remove a bit of code duplication. Insert bounds check when remove extra address info. --- Include/internal/pycore_code.h | 25 +++++++++++++++++++++++++ Objects/codeobject.c | 31 +++++-------------------------- Python/compile.c | 24 ++++++++++++------------ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 66dc447b7e7289..19b3f7221c770b 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -432,6 +432,31 @@ read_obj(uint16_t *p) return (PyObject *)val; } +static inline int +write_varint(uint8_t *ptr, int val) +{ + int written = 1; + while (val >= 64) { + *ptr++ = 64 | (val & 63); + val >>= 6; + written++; + } + *ptr = val; + return written; +} + +static inline int +write_signed_varint(uint8_t *ptr, int val) +{ + if (val < 0) { + val = ((-val)<<1) | 1; + } + else { + val = val << 1; + } + return write_varint(ptr, val); +} + #ifdef __cplusplus } #endif diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 1899e721d1cdd2..3ea85eb87b0c75 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -387,29 +387,6 @@ get_line_delta(const uint8_t *ptr) } } -static uint8_t * -write_varint(uint8_t *ptr, unsigned int val) -{ - while (val >= 64) { - *ptr++ = 64 | (val & 63); - val >>= 6; - } - *ptr++ = val; - return ptr; -} - -static uint8_t * -write_signed_varint(uint8_t *ptr, int val) -{ - if (val < 0) { - val = ((-val)<<1) | 1; - } - else { - val = val << 1; - } - return write_varint(ptr, val); -} - static PyObject * remove_column_info(PyObject *locations) { @@ -421,13 +398,15 @@ remove_column_info(PyObject *locations) return NULL; } uint8_t *output = (uint8_t *)PyBytes_AS_STRING(res); - while (offset < PyBytes_GET_SIZE(locations)) { + Py_ssize_t len = PyBytes_GET_SIZE(res); + while (offset < len) { Py_ssize_t len = PyBytes_GET_SIZE(res); Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); if (write_offset + 16 >= PyBytes_GET_SIZE(res)) { if (_PyBytes_Resize(&res, len * 2) < 0) { return NULL; } + len = PyBytes_GET_SIZE(res); output = (uint8_t *)PyBytes_AS_STRING(res) + write_offset; } int blength = data[offset] & 7; @@ -438,10 +417,10 @@ remove_column_info(PyObject *locations) else { int ldelta = get_line_delta(&data[offset]); *output++ = 0xe8 | blength; - output = write_signed_varint(output, ldelta); + output += write_signed_varint(output, ldelta); } offset++; - while ((data[offset] & 128) == 0) { + while (offset < len && (data[offset] & 128) == 0) { offset++; } } diff --git a/Python/compile.c b/Python/compile.c index 9df49c2b10aea2..e7284d08c3a0a0 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7473,26 +7473,26 @@ write_location_first_byte(struct assembler* a, int code, int length) write_location_byte(a, MSB | (code << 3) | (length - 1)); } +static uint8_t * +location_pointer(struct assembler* a) +{ + return (uint8_t *)PyBytes_AS_STRING(a->a_linetable) + + a->a_location_off; +} + static void write_location_varint(struct assembler* a, unsigned int val) { - while (val >= 64) { - write_location_byte(a, 64 | (val & 63)); - val >>= 6; - } - write_location_byte(a, val); + uint8_t *ptr = location_pointer(a); + a->a_location_off += write_varint(ptr, val); } + static void write_location_signed_varint(struct assembler* a, int val) { - if (val < 0) { - val = ((-val)<<1) | 1; - } - else { - val = val << 1; - } - write_location_varint(a, val); + uint8_t *ptr = location_pointer(a); + a->a_location_off += write_signed_varint(ptr, val); } static void From 3299d06d34517f24a5c540c326770e07655b80de Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 19 Apr 2022 14:02:56 +0100 Subject: [PATCH 16/18] Fix handling of missing endline in location info. --- Include/internal/pycore_code.h | 2 +- Python/compile.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 19b3f7221c770b..f465af7e636178 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -433,7 +433,7 @@ read_obj(uint16_t *p) } static inline int -write_varint(uint8_t *ptr, int val) +write_varint(uint8_t *ptr, unsigned int val) { int written = 1; while (val >= 64) { diff --git a/Python/compile.c b/Python/compile.c index e7284d08c3a0a0..9da5d444e968ee 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7525,6 +7525,7 @@ write_location_info_long_form(struct assembler* a, struct instr* i, int length) assert(length > 0 && length <= 8); write_location_first_byte(a, PY_CODE_LOCATION_INFO_LONG, length); write_location_signed_varint(a, i->i_lineno - a->a_lineno); + assert(i->i_end_lineno >= i->i_lineno); write_location_varint(a, i->i_end_lineno - i->i_lineno); write_location_varint(a, i->i_col_offset+1); write_location_varint(a, i->i_end_col_offset+1); @@ -7565,7 +7566,7 @@ write_location_info_entry(struct assembler* a, struct instr* i, int isize) assert(column >= -1); assert(end_column >= -1); if (column < 0 || end_column < 0) { - if (i->i_end_lineno == i->i_lineno) { + if (i->i_end_lineno == i->i_lineno || i->i_end_lineno == -1) { write_location_info_no_column(a, isize, line_delta); a->a_lineno = i->i_lineno; return 1; From 6e0aef9f2a0c4486618b5b853e639136a2f7d5e8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 19 Apr 2022 16:24:48 +0100 Subject: [PATCH 17/18] Fix remove_column_info (correctly this time) --- Objects/codeobject.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 3ea85eb87b0c75..71490b526e4faa 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -398,15 +398,12 @@ remove_column_info(PyObject *locations) return NULL; } uint8_t *output = (uint8_t *)PyBytes_AS_STRING(res); - Py_ssize_t len = PyBytes_GET_SIZE(res); - while (offset < len) { - Py_ssize_t len = PyBytes_GET_SIZE(res); + while (offset < PyBytes_GET_SIZE(locations)) { Py_ssize_t write_offset = output - (uint8_t *)PyBytes_AS_STRING(res); if (write_offset + 16 >= PyBytes_GET_SIZE(res)) { - if (_PyBytes_Resize(&res, len * 2) < 0) { + if (_PyBytes_Resize(&res, PyBytes_GET_SIZE(res) * 2) < 0) { return NULL; } - len = PyBytes_GET_SIZE(res); output = (uint8_t *)PyBytes_AS_STRING(res) + write_offset; } int blength = data[offset] & 7; @@ -420,7 +417,8 @@ remove_column_info(PyObject *locations) output += write_signed_varint(output, ldelta); } offset++; - while (offset < len && (data[offset] & 128) == 0) { + while (offset < PyBytes_GET_SIZE(locations) && + (data[offset] & 128) == 0) { offset++; } } From b6b43211d281a8c33b257a432f5966c694f00d76 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 21 Apr 2022 14:44:31 +0100 Subject: [PATCH 18/18] Move some logic about location table layout to shared header. --- Include/internal/pycore_code.h | 9 +++++++++ Objects/codeobject.c | 9 +++++---- Python/compile.c | 14 +++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index f465af7e636178..3059db465e7d25 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -457,6 +457,15 @@ write_signed_varint(uint8_t *ptr, int val) return write_varint(ptr, val); } +static inline int +write_location_entry_start(uint8_t *ptr, int code, int length) +{ + assert((code & 15) == code); + *ptr = 128 | (code << 3) | (length - 1); + return 1; +} + + #ifdef __cplusplus } #endif diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 71490b526e4faa..9a578158827567 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -406,14 +406,15 @@ remove_column_info(PyObject *locations) } output = (uint8_t *)PyBytes_AS_STRING(res) + write_offset; } - int blength = data[offset] & 7; int code = (data[offset] >> 3) & 15; - if (code == 15) { - *output++ = 0xf8 | blength; + if (code == PY_CODE_LOCATION_INFO_NONE) { + *output++ = data[offset]; } else { + int blength = (data[offset] & 7)+1; + output += write_location_entry_start( + output, PY_CODE_LOCATION_INFO_NO_COLUMNS, blength); int ldelta = get_line_delta(&data[offset]); - *output++ = 0xe8 | blength; output += write_signed_varint(output, ldelta); } offset++; diff --git a/Python/compile.c b/Python/compile.c index 9da5d444e968ee..2fe7033c36c7f3 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7466,13 +7466,6 @@ write_location_byte(struct assembler* a, int val) } -static void -write_location_first_byte(struct assembler* a, int code, int length) -{ - assert((code & 15) == code); - write_location_byte(a, MSB | (code << 3) | (length - 1)); -} - static uint8_t * location_pointer(struct assembler* a) { @@ -7480,6 +7473,13 @@ location_pointer(struct assembler* a) a->a_location_off; } +static void +write_location_first_byte(struct assembler* a, int code, int length) +{ + a->a_location_off += write_location_entry_start( + location_pointer(a), code, length); +} + static void write_location_varint(struct assembler* a, unsigned int val) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy