File Coverage

blib/lib/Devel/Caller.pm
Criterion Covered Total %
statement 86 103 83.5
branch 44 80 55.0
condition 10 16 62.5
subroutine 12 12 100.0
pod 4 5 80.0
total 156 216 72.2


line stmt bran cond sub pod time code
1 1     1   69309 use strict;
  1         3  
  1         36  
2             package Devel::Caller;
3 1     1   5 use warnings;
  1         2  
  1         38  
4 1     1   7 use B qw( peekop );
  1         1  
  1         57  
5 1     1   521 use PadWalker ();
  1         695  
  1         26  
6 1     1   6 use XSLoader;
  1         1  
  1         22  
7 1     1   5 use base qw( Exporter );
  1         2  
  1         142  
8 1     1   18 use 5.008;
  1         3  
9              
10             our $VERSION = '2.07';
11             XSLoader::load __PACKAGE__, $VERSION;
12              
13             our @EXPORT_OK = qw( caller_cv caller_args caller_vars called_with called_as_method );
14              
15             sub caller_cv {
16 52     52 1 963 my $level = shift;
17 52         108 my $cx = PadWalker::_upcontext($level + 1);
18 52 100       142 return unless $cx;
19 8         26 return _context_cv($cx);
20             }
21              
22             our $DEBUG = 0;
23              
24             # scan forward through the ops noting the pushmark or a padrange ops.
25             # These indicate the start of a subroutine call. We're looking for the most
26             # recent one before the subroutine invocation (the entersub).
27             sub scan_forward {
28 47     47 0 75 my $op = shift;
29 47 50       338 die "was expecting a pushmark or a padrange, not a " . $op->name
30             if ($op->name !~ /^(?:pushmark|padrange)$/);
31              
32 47         84 my @stack;
33 47   66     245 for (; $op && $op->name ne 'entersub'; $op = $op->next) {
34 313 50       556 print "SCAN ", peekop($op), "\n" if $DEBUG;
35 313 100 66     1873 if ($op->name eq "pushmark" or $op->name eq "padrange") {
36 97 50       201 print " PUSH\n" if $DEBUG;
37 97         494 push @stack, $op;
38             }
39 0         0 elsif (0) { # op consumes a mark
40             print " POP\n" if $DEBUG;
41             pop @stack;
42             }
43             }
44 47         104 return pop @stack;
45             }
46              
47             *caller_vars = \&called_with;
48             sub called_with {
49 47     47 1 27477 my $level = shift;
50 47         71 my $want_names = shift;
51              
52 47         202 my $op = _context_op( PadWalker::_upcontext( $level + 1 ));
53 47         123 my $cv = caller_cv( $level + 2 );
54 47 100       164 my $pad = $cv ? B::svref_2object( $cv )->PADLIST : B::comppadlist;
55 47         120 my $padn = $pad->ARRAYelt( 0 );
56 47         97 my $padv = $pad->ARRAYelt( 1 );
57              
58 47 50       89 print "Context OP: ", peekop($op), "\n" if $DEBUG;
59 47         89 $op = scan_forward( $op );
60 47 50       88 print "Scanned forward to ", peekop($op), "\n" if $DEBUG;
61              
62 47         75 my @return;
63             my $prev;
64              
65             # We're scanning through looking for ops which are pushing
66             # variables onto the stack (/pad(sv|av|hv)/ push from the pad,
67             # /gvsv|rv2([ahg]v/ are from globs.
68 47   50     178 for (; $op && $op->name ne 'entersub'; ($prev = $op) && ($op = $op->next)) {
      66        
69 224 50       462 printf "Loop: %s %s targ: %d\n", peekop($op), $op->name, $op->targ if $DEBUG;
70              
71 224 50       580 if ($op->name eq "padrange") {
72             # A padrange is a 5.17 optimisation that uses a single op to
73             # load multiple pad variables onto the stack. The old ops
74             # are preserved and are reachable as the padrange's sibling
75             # so that B::Deparse can pessimise it back to that state.
76             #
77             # http://perl5.git.perl.org/perl.git/commitdiff/0fe870f5
78             # http://perl5.git.perl.org/perl.git/commitdiff/a7fd8ef6
79             #
80             # We could use the B::Deparse method, but it's probably simpler if
81             # we just reassign $op.
82 0 0       0 print "padrange, diverting down ", $prev->sibling, "\n" if $DEBUG;
83 0         0 $op = $op->sibling;
84             }
85              
86 224 50       2006 if ($op->name =~ /padsv_store/) {
    100          
    100          
    100          
87             # A padsv_store is a 5.37 optimization that combines a padsv and
88             # an sassign into a single op. The new op steals the targ slot
89             # of the original padsv.
90             #
91             # https://github.com/Perl/perl5/commit/9fdd7fc
92 0 0       0 print "Copying from pad\n" if $DEBUG;
93 0 0       0 if ($want_names) {
94 0         0 push @return, $padn->ARRAYelt( $op->targ )->PVX;
95             }
96             else {
97 0         0 push @return, $padv->ARRAYelt( $op->targ )->object_2svref;
98             }
99 0         0 next;
100             }
101             elsif ($op->name =~ "pad(sv|av|hv)") {
102 50 100       304 if ($op->next->next->name eq "sassign") {
    50          
103 6 50       13 print "sassign in two ops, this is the target skipping\n" if $DEBUG;
104 6         38 next;
105             } elsif ($op->next->name eq "padsv_store") {
106 0 0       0 print "padsv_store in one op, this is the target, skipping\n" if $DEBUG;
107 0         0 next;
108             }
109              
110 44 50       99 print "Copying from pad\n" if $DEBUG;
111 44 100       79 if ($want_names) {
112 41         184 push @return, $padn->ARRAYelt( $op->targ )->PVX;
113             }
114             else {
115 3         34 push @return, $padv->ARRAYelt( $op->targ )->object_2svref;
116             }
117 44         272 next;
118             }
119             elsif ($op->name =~ /gvsv|rv2(av|hv|gv)/) {
120 23 50       123 if ($op->next->next->name eq "sassign") {
121 0 0       0 print "sassign in two ops, this is the target, skipping\n" if $DEBUG;
122 0         0 next;
123             }
124              
125 23 100       73 my $consider = ($op->name eq "gvsv") ? $op : $prev;
126 23         37 my $gv;
127              
128 23 50       45 if (ref $consider eq 'B::PADOP') {
129 0 0       0 print "GV is really a padgv\n" if $DEBUG;
130 0         0 $gv = $padv->ARRAYelt( $consider->padix );
131 0 0       0 print "NEW GV $gv\n" if $DEBUG;
132             }
133             else {
134 23         58 $gv = $consider->gv;
135             }
136              
137 23 50       46 print "consider: $consider ", $consider->name, " gv $gv\n"
138             if $DEBUG;
139              
140 23 100       44 if ($want_names) {
141 20         58 my %sigils = (
142             "gvsv" => '$',
143             "rv2av" => '@',
144             "rv2hv" => '%',
145             "rv2gv" => '*',
146             );
147              
148 20         196 push @return, $sigils{ $op->name } . $gv->STASH->NAME . "::" . $gv->SAFENAME;
149             }
150             else {
151 3         15 my %slots = (
152             "gvsv" => 'SCALAR',
153             "rv2av" => 'ARRAY',
154             "rv2hv" => 'HASH',
155             "rv2gv" => 'GLOB',
156             );
157 3         7 push @return, *{ $gv->object_2svref }{ $slots{ $op->name} };
  3         24  
158             }
159              
160 23         167 next;
161             }
162             elsif ($op->name eq "const") {
163 6 100       42 if ($op->next->next->name eq "sassign") {
    50          
164 3 50       9 print "sassign in two ops, this is the target, skipping\n" if $DEBUG;
165 3         19 next;
166             } elsif ($op->next->name eq "padsv_store") {
167 0 0       0 print "padsv_store in one op, this is the target, skipping\n" if $DEBUG;
168 0         0 next;
169             }
170              
171 3 50       8 push @return, $want_names ? undef : $op->sv;
172 3         20 next;
173             }
174             }
175 47         279 return @return;
176             }
177              
178              
179             sub called_as_method {
180 3   50 3 1 1523 my $level = shift || 0;
181 3         18 my $op = _context_op( PadWalker::_upcontext( $level + 1 ));
182              
183 3 50       10 print "called_as_method: $op\n" if $DEBUG;
184 3 50       19 die "was expecting a pushmark or pad, not a ". $op->name
185             unless $op->name eq "pushmark";
186 3   66     24 while (($op = $op->next) && ($op->name ne "entersub")) {
187 6 50       14 print "method: ", $op->name, "\n" if $DEBUG;
188 6 100       79 return 1 if $op->name =~ /^method(?:_named)?$/;
189             }
190 1         8 return;
191             }
192              
193              
194             sub caller_args {
195 1     1 1 534 my $level = shift;
196             package DB;
197 1         11 () = caller( $level + 1 );
198             return @DB::args
199 1         6 }
200              
201             1;
202             __END__