Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

GH-88116: Use a compact format to represent end line and column offsets. #91666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Get compact debug table (almost) working.
  • Loading branch information
markshannon committed Apr 14, 2022
commit f603e3f851ce9e2c6fc3bf48a45bb6e06fe8745b
8 changes: 4 additions & 4 deletions 8 Lib/test/test_marshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
188 changes: 133 additions & 55 deletions 188 Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
markshannon marked this conversation as resolved.
Show resolved Hide resolved
markshannon marked this conversation as resolved.
Show resolved Hide resolved
*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 *
Expand All @@ -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);
Expand All @@ -389,7 +505,7 @@ _PyCode_New(struct _PyCodeConstructor *con)
return NULL;
}
init_code(co, con);

Py_XDECREF(replacement_locations);
return co;
}

Expand Down Expand Up @@ -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;
}


Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion 6 Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions 2 Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions 1 Python/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.