1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
from .varint import read_signed_varint, read_varint, generate_signed_varint
def generate_linetable(pairs, firstlineno=1, use_bytecode_offset=False):
return b"".join(_generate_linetable(pairs, firstlineno, use_bytecode_offset))
def _generate_linetable(pairs, firstlineno, use_bytecode_offset):
pairs = iter(pairs) # will do nothing if it's already an iterator.
cur_line = firstlineno
cur_entry = next(pairs)
while cur_entry:
try:
next_entry = next(pairs)
except StopIteration:
next_entry = None
length, start_line, *more = cur_entry
if more:
end_line, start_col, end_col = more
else:
end_line, start_col, end_col = start_line, None, None
if use_bytecode_offset:
# We don't have the length,
# but we have the byte code offsets from dis.findlinestarts()
length = _linetable_length(length, next_entry)
if start_line is not None:
line_delta = start_line - cur_line
cur_line = end_line
if start_line is None:
code = 15
yield _new_linetable_entry(code, length).to_bytes(1, byteorder="little")
elif start_col is None:
code = 13
yield _new_linetable_entry(code, length).to_bytes(1, byteorder="little")
for b in generate_signed_varint(line_delta):
yield b.to_bytes(1, byteorder="little")
elif line_delta == 0 and (end_col - start_col) < 15:
# short form, same line as before and near columns.
code = start_col // 8
yield _new_linetable_entry(code, length).to_bytes(1, byteorder="little")
yield (((start_col % 8) << 4) | (end_col - start_col)).to_bytes(1, byteorder="little")
elif line_delta <= 2 and start_col <= 255 and end_col <= 255:
# New line form
code = 10 + line_delta
yield _new_linetable_entry(code, length).to_bytes(1, byteorder="little")
yield start_col.to_bytes(1, byteorder="little")
yield end_col.to_bytes(1, byteorder="little")
else:
raise NotImplementedError()
cur_entry = next_entry
def _new_linetable_entry(code, length):
# 8 bits entry made of:
# -----------------
# 7 | 6 - 3 | 2 - 0
# 1 | code | length-1
return (1 << 7) | (code << 3) | (length - 1)
def _linetable_length(bc_start, next_entry):
length = 1
if next_entry:
# Each bytecode entry is 2 bytes,
# so compute the offset in bytes between two bytecode entries
# and then divide by 2 to get the number of entries.
length = (next_entry[0] - bc_start) // 2
return length
def parse_linetable(linetable, firstlineno=1):
line = firstlineno
it = iter(linetable)
while True:
try:
first_byte = next(it)
except StopIteration:
return
code = (first_byte >> 3) & 15
length = (first_byte & 7) + 1
if code == 15:
yield (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 (length, line, end_line, col, end_col)
elif code == 13: # No column
line_delta = read_signed_varint(it)
line += line_delta
yield (length, line, line, None, None)
elif code in (10, 11, 12): # new line
line_delta = code - 10
line += line_delta
column = next(it)
end_column = next(it)
yield (length, line, line, column, end_column)
elif 0 <= code < 10: # short form
second_byte = next(it)
column = code << 3 | (second_byte >> 4)
yield (length, line, line, column, column + (second_byte & 15))
else:
raise NotImplementedError()
|