1
1
All about co_lnotab, the line number table.
2
2
3
3
Code objects store a field named co_lnotab. This is an array of unsigned bytes
4
- disguised as a Python string. It is used to map bytecode offsets to source code
5
- line #s for tracebacks and to identify line number boundaries for line tracing.
4
+ disguised as a Python bytes object. It is used to map bytecode offsets to
5
+ source code line #s for tracebacks and to identify line number boundaries for
6
+ line tracing.
6
7
7
8
The array is conceptually a compressed list of
8
9
(bytecode offset increment, line number increment)
9
10
pairs. The details are important and delicate, best illustrated by example:
10
11
11
12
byte code offset source code line number
12
- 0 1
13
- 6 2
14
- 50 7
13
+ 0 1
14
+ 6 2
15
+ 50 7
15
16
350 207
16
17
361 208
17
18
@@ -24,7 +25,8 @@ look like:
24
25
The above doesn't really work, but it's a start. An unsigned byte (byte code
25
26
offset) can't hold negative values, or values larger than 255, a signed byte
26
27
(line number) can't hold values larger than 127 or less than -128, and the
27
- above example contains two such values. So we make two tweaks:
28
+ above example contains two such values. (Note that before 3.6, line number
29
+ was also encoded by an unsigned byte.) So we make two tweaks:
28
30
29
31
(a) there's a deep assumption that byte code offsets increase monotonically,
30
32
and
@@ -52,7 +54,7 @@ the example above, assemble_lnotab in compile.c should not (as was actually done
52
54
until 2.2) expand 300, 200 to
53
55
255, 255, 45, 45,
54
56
but to
55
- 255, 0, 45, 128 , 0, 72 .
57
+ 255, 0, 45, 127 , 0, 73 .
56
58
57
59
The above is sufficient to reconstruct line numbers for tracebacks, but not for
58
60
line tracing. Tracing is handled by PyCode_CheckLineNumber() in codeobject.c
@@ -83,30 +85,34 @@ Consider this code:
83
85
84
86
1: def f(a):
85
87
2: while a:
86
- 3: print 1,
88
+ 3: print(1)
87
89
4: break
88
90
5: else:
89
- 6: print 2,
91
+ 6: print(2)
90
92
91
93
which compiles to this:
92
94
93
- 2 0 SETUP_LOOP 19 (to 22 )
94
- >> 3 LOAD_FAST 0 (a)
95
- 6 POP_JUMP_IF_FALSE 17
95
+ 2 0 SETUP_LOOP 26 (to 28 )
96
+ >> 2 LOAD_FAST 0 (a)
97
+ 4 POP_JUMP_IF_FALSE 18
96
98
97
- 3 9 LOAD_CONST 1 (1)
98
- 12 PRINT_ITEM
99
+ 3 6 LOAD_GLOBAL 0 (print)
100
+ 8 LOAD_CONST 1 (1)
101
+ 10 CALL_FUNCTION 1
102
+ 12 POP_TOP
99
103
100
- 4 13 BREAK_LOOP
101
- 14 JUMP_ABSOLUTE 3
102
- >> 17 POP_BLOCK
104
+ 4 14 BREAK_LOOP
105
+ 16 JUMP_ABSOLUTE 2
106
+ >> 18 POP_BLOCK
103
107
104
- 6 18 LOAD_CONST 2 (2)
105
- 21 PRINT_ITEM
106
- >> 22 LOAD_CONST 0 (None)
107
- 25 RETURN_VALUE
108
+ 6 20 LOAD_GLOBAL 0 (print)
109
+ 22 LOAD_CONST 2 (2)
110
+ 24 CALL_FUNCTION 1
111
+ 26 POP_TOP
112
+ >> 28 LOAD_CONST 0 (None)
113
+ 30 RETURN_VALUE
108
114
109
- If 'a' is false, execution will jump to the POP_BLOCK instruction at offset 17
115
+ If 'a' is false, execution will jump to the POP_BLOCK instruction at offset 18
110
116
and the co_lnotab will claim that execution has moved to line 4, which is wrong.
111
117
In this case, we could instead associate the POP_BLOCK with line 5, but that
112
118
would break jumps around loops without else clauses.
0 commit comments