]> granicus.if.org Git - python/commitdiff
backport of r60575 (issue #1750076): Debugger did not step on every iteration of...
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 4 Feb 2008 22:34:57 +0000 (22:34 +0000)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 4 Feb 2008 22:34:57 +0000 (22:34 +0000)
The mapping between bytecode offsets and source lines (lnotab) did not contain
an entry for the beginning of the loop.

Now it does, and the lnotab can be a bit larger:
in particular, several statements on the same line generate several entries.
However, this does not bother the settrace function, which will trigger only
one 'line' event.

The lnotab seems to be exactly the same as with python2.4.

Lib/test/test_trace.py
Misc/NEWS
Python/compile.c

index 57c3da7ca5f27270ce9409ca1f3ce4f504065b6f..64dca9fd0cd9f3da90ab21cd8a084e845ed491bf 100644 (file)
@@ -252,14 +252,16 @@ class TraceTestCase(unittest.TestCase):
                 "\n".join(difflib.ndiff([str(x) for x in expected_events],
                                         [str(x) for x in events])))
 
-
-    def run_test(self, func):
+    def run_and_compare(self, func, events):
         tracer = Tracer()
         sys.settrace(tracer.trace)
         func()
         sys.settrace(None)
         self.compare_events(func.func_code.co_firstlineno,
-                            tracer.events, func.events)
+                            tracer.events, events)
+
+    def run_test(self, func):
+        self.run_and_compare(func, func.events)
 
     def run_test2(self, func):
         tracer = Tracer()
@@ -307,6 +309,49 @@ class TraceTestCase(unittest.TestCase):
         self.compare_events(generator_example.func_code.co_firstlineno,
                             tracer.events, generator_example.events)
 
+    def test_14_onliner_if(self):
+        def onliners():
+            if True: False
+            else: True
+            return 0
+        self.run_and_compare(
+            onliners,
+            [(0, 'call'),
+             (1, 'line'),
+             (3, 'line'),
+             (3, 'return')])
+
+    def test_15_loops(self):
+        # issue1750076: "while" expression is skipped by debugger
+        def for_example():
+            for x in range(2):
+                pass
+        self.run_and_compare(
+            for_example,
+            [(0, 'call'),
+             (1, 'line'),
+             (2, 'line'),
+             (1, 'line'),
+             (2, 'line'),
+             (1, 'line'),
+             (1, 'return')])
+
+        def while_example():
+            # While expression should be traced on every loop
+            x = 2
+            while x > 0:
+                x -= 1
+        self.run_and_compare(
+            while_example,
+            [(0, 'call'),
+             (2, 'line'),
+             (3, 'line'),
+             (4, 'line'),
+             (3, 'line'),
+             (4, 'line'),
+             (3, 'line'),
+             (3, 'return')])
+
 class RaisingTraceFuncTestCase(unittest.TestCase):
     def trace(self, frame, event, arg):
         """A trace function that raises an exception in response to a
index 5cd8e4ab7e6684c1276170cbebdb063c0cc8923d..c9b86e216611af389c20576a5cf01ecc2eae1fff 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -80,6 +80,10 @@ Core and builtins
 Library
 -------
 
+- #175006: The debugger used to skip the condition of a "while" statement
+  after the first iteration. Now it correctly steps on the expression, and
+  breakpoints on the "while" statement are honored on each loop.
+
 - The ctypes int types did not accept objects implementing
   __int__() in the constructor.
 
index 8e96ddfa62c9a1b9c9e5933f3cb47c803d526c67..6df09dc53c08652cda353b646ad36db9e57ede65 100644 (file)
@@ -1298,11 +1298,16 @@ compiler_next_instr(struct compiler *c, basicblock *b)
        return b->b_iused++;
 }
 
-/* Set the i_lineno member of the instruction at offse off if the
-   line number for the current expression/statement (?) has not
+/* Set the i_lineno member of the instruction at offset off if the
+   line number for the current expression/statement has not
    already been set.  If it has been set, the call has no effect.
 
-   Every time a new node is b
+   The line number is reset in the following cases:
+   - when entering a new scope
+   - on each statement
+   - on each expression that start a new line
+   - before the "except" clause
+   - before the "for" and "while" expressions
    */
 
 static void
@@ -2234,9 +2239,8 @@ compiler_for(struct compiler *c, stmt_ty s)
        VISIT(c, expr, s->v.For.iter);
        ADDOP(c, GET_ITER);
        compiler_use_next_block(c, start);
-       /* XXX(nnorwitz): is there a better way to handle this?
-          for loops are special, we want to be able to trace them
-          each time around, so we need to set an extra line number. */
+       /* for expressions must be traced on each iteration,
+          so we need to set an extra line number. */
        c->u->u_lineno_set = false;
        ADDOP_JREL(c, FOR_ITER, cleanup);
        VISIT(c, expr, s->v.For.target);
@@ -2283,6 +2287,9 @@ compiler_while(struct compiler *c, stmt_ty s)
        if (!compiler_push_fblock(c, LOOP, loop))
                return 0;
        if (constant == -1) {
+               /* while expressions must be traced on each iteration,
+                  so we need to set an extra line number. */
+               c->u->u_lineno_set = false;
                VISIT(c, expr, s->v.While.test);
                ADDOP_JREL(c, JUMP_IF_FALSE, anchor);
                ADDOP(c, POP_TOP);
@@ -2464,8 +2471,8 @@ compiler_try_except(struct compiler *c, stmt_ty s)
                                                s->v.TryExcept.handlers, i);
                if (!handler->type && i < n-1)
                    return compiler_error(c, "default 'except:' must be last");
-        c->u->u_lineno_set = false;
-        c->u->u_lineno = handler->lineno;
+               c->u->u_lineno_set = false;
+               c->u->u_lineno = handler->lineno;
                except = compiler_new_block(c);
                if (except == NULL)
                        return 0;
@@ -4184,12 +4191,6 @@ assemble_lnotab(struct assembler *a, struct instr *i)
        assert(d_bytecode >= 0);
        assert(d_lineno >= 0);
 
-       /* XXX(nnorwitz): is there a better way to handle this?
-          for loops are special, we want to be able to trace them
-          each time around, so we need to set an extra line number. */
-       if (d_lineno == 0 && i->i_opcode != FOR_ITER)
-               return 1;
-
        if (d_bytecode > 255) {
                int j, nbytes, ncodes = d_bytecode / 255;
                nbytes = a->a_lnotab_off + 2 * ncodes;