All about co_lnotab, the line number table.Code objects store a field named co_lnotab. This is an array of unsigned bytesdisguised as a Python bytes object. It is used to map bytecode offsets tosource code line #s for tracebacks and to identify line number boundaries forline tracing. Because of internals of the peephole optimizer, it's possiblefor lnotab to contain bytecode offsets that are no longer valid (for exampleif the optimizer removed the last line in a function).The array is conceptually a compressed list of(bytecode offset increment, line number increment)pairs. The details are important and delicate, best illustrated by example:byte code offset source code line number0 16 250 7350 207361 208Instead of storing these numbers literally, we compress the list by storing onlythe difference from one row to the next. Conceptually, the stored list mightlook like:0, 1, 6, 1, 44, 5, 300, 200, 11, 1The above doesn't really work, but it's a start. An unsigned byte (byte codeoffset) can't hold negative values, or values larger than 255, a signed byte(line number) can't hold values larger than 127 or less than -128, and theabove example contains two such values. (Note that before 3.6, line numberwas also encoded by an unsigned byte.) So we make two tweaks:(a) there's a deep assumption that byte code offsets increase monotonically,and(b) if byte code offset jumps by more than 255 from one row to the next, or ifsource code line number jumps by more than 127 or less than -128 from one rowto the next, more than one pair is written to the table. In case #b,there's no way to know from looking at the table later how many were written.That's the delicate part. A user of co_lnotab desiring to find the sourceline number corresponding to a bytecode address A should do something likethis:lineno = addr = 0for addr_incr, line_incr in co_lnotab:addr += addr_incrif addr > A:return linenoif line_incr >= 0x80:line_incr -= 0x100lineno += line_incr(In C, this is implemented by PyCode_Addr2Line().) In order for this to work,when the addr field increments by more than 255, the line # increment in eachpair generated must be 0 until the remaining addr increment is < 256. So, inthe example above, assemble_lnotab in compile.c should not (as was actually doneuntil 2.2) expand 300, 200 to255, 255, 45, 45,but to255, 0, 45, 127, 0, 73.The above is sufficient to reconstruct line numbers for tracebacks, but not forline tracing. Tracing is handled by PyCode_CheckLineNumber() in codeobject.cand maybe_call_line_trace() in ceval.c.*** Tracing ***To a first approximation, we want to call the tracing function when the linenumber of the current instruction changes. Re-computing the current line forevery instruction is a little slow, though, so each time we compute the linenumber we save the bytecode indices where it's valid:*instr_lb <= frame->f_lasti < *instr_ubis true so long as execution does not change lines. That is, *instr_lb holdsthe first bytecode index of the current line, and *instr_ub holds the firstbytecode index of the next line. As long as the above expression is true,maybe_call_line_trace() does not need to call PyCode_CheckLineNumber(). Notethat the same line may appear multiple times in the lnotab, either because thebytecode jumped more than 255 indices between line number changes or becausethe compiler inserted the same line twice. Even in that case, *instr_ub holdsthe first index of the next line.However, we don't *always* want to call the line trace function when the abovetest fails.Consider this code:1: def f(a):2: while a:3: print(1)4: break5: else:6: print(2)which compiles to this:2 0 SETUP_LOOP 26 (to 28)>> 2 LOAD_FAST 0 (a)4 POP_JUMP_IF_FALSE 183 6 LOAD_GLOBAL 0 (print)8 LOAD_CONST 1 (1)10 CALL_FUNCTION 112 POP_TOP4 14 BREAK_LOOP16 JUMP_ABSOLUTE 2>> 18 POP_BLOCK6 20 LOAD_GLOBAL 0 (print)22 LOAD_CONST 2 (2)24 CALL_FUNCTION 126 POP_TOP>> 28 LOAD_CONST 0 (None)30 RETURN_VALUEIf 'a' is false, execution will jump to the POP_BLOCK instruction at offset 18and the co_lnotab will claim that execution has moved to line 4, which is wrong.In this case, we could instead associate the POP_BLOCK with line 5, but thatwould break jumps around loops without else clauses.We fix this by only calling the line trace function for a forward jump if theco_lnotab indicates we have jumped to the *start* of a line, i.e. if the currentinstruction offset matches the offset given for the start of a line by theco_lnotab. For backward jumps, however, we always call the line trace function,which lets a debugger stop on every evaluation of a loop guard (which usuallywon't be the first opcode in a line).Why do we set f_lineno when tracing, and only just before calling the tracefunction? Well, consider the code above when 'a' is true. If stepping throughthis with 'n' in pdb, you would stop at line 1 with a "call" type event, thenline events on lines 2, 3, and 4, then a "return" type event -- but because thecode for the return actually falls in the range of the "line 6" opcodes, youwould be shown line 6 during this event. This is a change from the behaviour in2.2 and before, and I've found it confusing in practice. By setting and usingf_lineno when tracing, one can report a line number different from thatsuggested by f_lasti on this one occasion where it's desirable.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。