[perl #118839] Make ā€˜nā€™ debugger cmd respect lv subs
authorFather Chrysostomos <sprout@cpan.org>
Sat, 13 Jul 2013 18:41:44 +0000 (11:41 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 13 Jul 2013 18:49:14 +0000 (11:49 -0700)
The ā€˜nā€™ debugger command, which is supposed to step over subs, was not
stepping over lvalue subs.

This is a regression from 5.8, but 5.8 was worse.

This bug did not occur in 5.8 because there was no mechanism back then
for handling lvalue subs specially (via DB::lsub), so assignment to an
lvalue sub was completely broken under the debugger.

Perl 5.10 introduced DB::lsub.  The implementation in perl5db.pl
contains these lines at the end of the sub:

    # Pop the single-step value back off the stack.
    $single |= $stack[ $stack_depth-- ];

    # call the original lvalue sub.
    &$sub;

The regular DB::sub does this:

        {
            no strict 'refs';
            @ret = &$sub;
        }

        # Pop the single-step value back off the stack.
        $single |= $stack[ $stack_depth-- ];

Notice how $single (i.e., $DB::single) is modified before and after
the sub call, respectively.  The order is different.

There are two types of lvalue list context for lvalue subs.  (foo)=3
will allow sub foo:lvalue{@a} to return the array itself.  \(foo) and
bar(foo) will flatten the array.

So there is no way in pure Perl to capture the return value and have
it returned to the caller unchanged.  That is why DB::lsub has to call
the sub last of all (and have the context propagated).

The solution here is to use localisation to accomplish the assigment
to $single after the lvalue sub exits.

MANIFEST
lib/perl5db.pl
lib/perl5db.t
lib/perl5db/t/lsub-n [new file with mode: 0644]

index a676881..de8c16f 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -4169,6 +4169,7 @@ lib/perl5db/t/eval-line-bug       Tests for the Perl debugger
 lib/perl5db/t/fact             Tests for the Perl debugger
 lib/perl5db/t/filename-line-breakpoint         Tests for the Perl debugger
 lib/perl5db/t/load-modules     Tests for the Perl debugger
+lib/perl5db/t/lsub-n           Test script used by perl5db.t
 lib/perl5db/t/lvalue-bug       Tests for the Perl debugger
 lib/perl5db/t/MyModule.pm      Tests for the Perl debugger
 lib/perl5db/t/proxy-constants  Tests for the Perl debugger
index f68a0d2..48ec301 100644 (file)
@@ -4266,7 +4266,9 @@ sub lsub : lvalue {
     $stack[-1] = $single;
 
     # Turn off all flags except single-stepping.
-    $single &= 1;
+    # Use local so the single-step value is popped back off the
+    # stack for us.
+    local $single = $single & 1;
 
     # If we've gotten really deeply recursed, turn on the flag that will
     # make us stop with the 'deep recursion' message.
@@ -4275,9 +4277,6 @@ sub lsub : lvalue {
     # If frame messages are on ...
     _print_frame_message($al);
 
-    # Pop the single-step value back off the stack.
-    $single |= $stack[ $stack_depth-- ];
-
     # call the original lvalue sub.
     &$sub;
 }
index 6ab4694..26b0581 100644 (file)
@@ -29,7 +29,7 @@ BEGIN {
     $ENV{PERL_RL} = 'Perl'; # Suppress system Term::ReadLine::Gnu
 }
 
-plan(116);
+plan(117);
 
 my $rc_filename = '.perldb';
 
@@ -1044,6 +1044,20 @@ sub _calc_trace_wrapper
     );
 }
 
+# Test for n with lvalue subs
+DebugWrap->new({
+    cmds =>
+    [
+        'n', 'print "<$x>"',
+        'n', 'print "<$x>"',
+        'q',
+    ],
+    prog => '../lib/perl5db/t/lsub-n',
+})->output_like(
+    qr/<1><11>/,
+    'n steps over lvalue subs',
+);
+
 # Test for 'M' (module list).
 {
     my $wrapper = DebugWrap->new(
diff --git a/lib/perl5db/t/lsub-n b/lib/perl5db/t/lsub-n
new file mode 100644 (file)
index 0000000..83257b4
--- /dev/null
@@ -0,0 +1,6 @@
+sub foo : lvalue {
+    $x+=10;
+}
+$x++;
+foo();
+$x+=100;