summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c64
1 files changed, 54 insertions, 10 deletions
diff --git a/Python/compile.c b/Python/compile.c
index ec3de26..b9ba0fc 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;
}