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) {