diff options
-rw-r--r-- | Python/compile.c | 64 |
1 files changed, 54 insertions, 10 deletions
diff --git a/Python/compile.c b/Python/compile.c index 86147cf..7af8433 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -336,6 +336,50 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags, c_argcount, c_globals, and c_flags. */ +/* All about c_lnotab. + +c_lnotab is an array of unsigned bytes disguised as a Python string. In -O +mode, SET_LINENO opcodes aren't generated, and bytecode offsets are mapped +to source code line #s (when needed for tracebacks) via c_lnotab instead. +The array is conceptually a 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 number + 0 1 + 6 2 + 50 7 + 350 307 + 361 308 + +The first trick is that these numbers aren't stored, only the increments +from one row to the next (this doesn't really work, but it's a start): + + 0, 1, 6, 1, 44, 5, 300, 300, 11, 1 + +The second trick is that an unsigned byte can't hold negative values, or +values larger than 255, so (a) there's a deep assumption that byte code +offsets and their corresponding line #s both increase monotonically, and (b) +if at least one column jumps by more than 255 from one row to 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 c_lnotab desiring to find the source line number +corresponding to a bytecode address A should do something like this + + lineno = addr = 0 + for addr_incr, line_incr in c_lnotab: + addr += addr_incr + if addr > A: + return lineno + lineno += line_incr + +In order for this to work, when the addr field increments by more than 255, +the line # increment in each pair generated must be 0 until the remaining addr +increment is < 256. So, in the example above, com_set_lineno should not (as +was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to +255, 0, 45, 255, 0, 45. +*/ + struct compiling { PyObject *c_code; /* string */ PyObject *c_consts; /* list of objects */ @@ -692,17 +736,17 @@ com_set_lineno(struct compiling *c, int lineno) else { int incr_addr = c->c_nexti - c->c_last_addr; int incr_line = lineno - c->c_last_line; - while (incr_addr > 0 || incr_line > 0) { - int trunc_addr = incr_addr; - int trunc_line = incr_line; - if (trunc_addr > 255) - trunc_addr = 255; - if (trunc_line > 255) - trunc_line = 255; - com_add_lnotab(c, trunc_addr, trunc_line); - incr_addr -= trunc_addr; - incr_line -= trunc_line; + while (incr_addr > 255) { + com_add_lnotab(c, 255, 0); + incr_addr -= 255; + } + while (incr_line > 255) { + com_add_lnotab(c, incr_addr, 255); + incr_line -=255; + incr_addr = 0; } + if (incr_addr > 0 || incr_line > 0) + com_add_lnotab(c, incr_addr, incr_line); c->c_last_addr = c->c_nexti; c->c_last_line = lineno; } |