File Coverage

blib/lib/Devel/Chitin/Stack.pm
Criterion Covered Total %
statement 13 88 14.7
branch 0 28 0.0
condition 0 17 0.0
subroutine 5 19 26.3
pod 5 7 71.4
total 23 159 14.4


line stmt bran cond sub pod time code
1             package Devel::Chitin::Stack;
2              
3 34     34   226 use strict;
  34         71  
  34         1039  
4 34     34   174 use warnings;
  34         77  
  34         2151  
5              
6             our $VERSION = '0.15';
7              
8             our @saved_ARGV;
9             BEGIN {
10 34     34   46147 @saved_ARGV = @ARGV;
11             }
12              
13             my @caller_values = qw(package filename line subroutine hasargs wantarray
14             evaltext is_require hints bitmask);
15              
16             # DB::DB will wipe this out between stoppages.
17             our $stack_object;
18              
19 0     0 0   sub invalidate { undef($stack_object) }
20              
21             sub new {
22 0     0 0   my $class = shift;
23 0 0         return $stack_object if $stack_object;
24              
25 0           my @frames;
26 0           my $in_debugger_frames = 1;
27 0           my $next_AUTOLOAD_idx = 0;
28 0           my $serial_iter = _serial_iterator();
29 0           my @prev_loc;
30              
31             my $level;
32 0           for($level = 0; ; $level++) {
33 0           my %caller;
34 0           do {
35             package DB;
36 0           @caller{@caller_values} = caller($level);
37             };
38 0 0         last unless defined($caller{line}); # no more frames
39              
40             {
41 0           my @this = @caller{'filename','line','package'};
  0            
42 0           @caller{'filename','line','package'} = @prev_loc;
43 0           @prev_loc = @this;
44             }
45              
46 0 0         if ($caller{subroutine} eq 'DB::DB') {
47             # entered the debugger here, start over recording frames
48 0           $in_debugger_frames = 0;
49 0           next;
50             }
51 0 0         next if $in_debugger_frames;
52              
53             #next if $skip;
54              
55 0           $caller{args} = [ @DB::args ];
56 0           $caller{callsite} = Devel::Chitin::Location::get_callsite($level);
57              
58             # subname is the subroutine without the package part
59 0 0         $caller{subname} = $caller{subroutine} =~ m/\b(\w+$|__ANON__)/ ? $1 : $caller{subroutine};
60 0 0         if ($caller{subname} eq 'AUTOLOAD') {
61             # needs support in DB::sub for storing the names of AUTOLOADed subs
62 0           my($autoload) = $DB::AUTOLOAD_names[ $next_AUTOLOAD_idx++ ] =~ m/::(\w+)$/;
63 0           $caller{autoload} = $autoload;
64             } else {
65 0           $caller{autoload} = undef;
66             }
67              
68             # if it's a string eval, add info about what file and line the source string
69             # came from
70 0   0       @caller{'evalfile','evalline'} = ($caller{filename} || '') =~ m/\(eval \d+\)\[(.*?):(\d+)\]/;
71              
72             # perl 5.10.* and earlier use 0 for scalar context.
73             # Normalize this value to the empty string for scalar context
74 0 0 0       $caller{wantarray} = '' if (defined($caller{wantarray}) and !$caller{wantarray});
75              
76             # Normalize hasargs. eval-frames will always have 0. Subroutines called with the
77             # &subname; syntax will have '' returned from caller() starting with perl 5.12.
78 0 0 0       $caller{hasargs} = '' if (! $caller{hasargs} and $caller{subroutine} ne '(eval)');
79              
80 0           $caller{serial} = $serial_iter->(@caller{'subroutine','filename','line'});
81              
82 0           $caller{level} = $level;
83              
84 0           push @frames, Devel::Chitin::StackFrame->_new(%caller);
85             }
86              
87             # fab up a frame for the main program
88 0           push @frames, Devel::Chitin::StackFrame->_new(
89             'package' => 'main',
90             filename => $prev_loc[0],
91             line => $prev_loc[1],
92             subroutine => 'main::MAIN',
93             subname => 'MAIN',
94             'wantarray' => undef,
95             evaltext => undef,
96             evalfile => undef,
97             evalline => undef,
98             is_require => undef,
99             hints => '', # hints and bitmask here are just the values
100             bitmask => 256, # caller() always gives for the top-level caller
101             autoload => undef,
102             hasargs => 1,
103             args => \@saved_ARGV,
104             level => $level,
105             serial => $Devel::Chitin::stack_serial[0]->[-1],
106             callsite => undef,
107             );
108              
109 0           return $stack_object = bless \@frames, $class;
110             }
111              
112             sub _serial_iterator {
113 0     0     my $next_idx = $#Devel::Chitin::stack_serial;
114              
115             return sub {
116 0     0     my($subname, $filename, $line) = @_;
117              
118 0 0         return unless @Devel::Chitin::stack_serial;
119              
120 0 0         if (index($subname, '(eval') >= 0) {
121 0           my $this_sub_serial = $Devel::Chitin::stack_serial[$next_idx]->[1];
122 0   0       return $Devel::Chitin::eval_serial{$this_sub_serial}{$line} ||= DB::_allocate_sub_serial();
123             }
124              
125 0           for (my $i = $next_idx; $i >= 0; $i--) {
126 0 0 0       if ($subname eq $Devel::Chitin::stack_serial[$i]->[0]
      0        
127             or
128             (index($subname, '__ANON__[') >= 0 and ref($Devel::Chitin::stack_serial[$i]->[0]) eq 'CODE')
129             ) {
130 0           $next_idx = $i - 1;
131 0           return $Devel::Chitin::stack_serial[$i]->[-1];
132             }
133             }
134 0           return DB::_allocate_sub_serial(); # Punt by making a new one up
135 0           };
136             }
137              
138             sub depth {
139 0     0 1   my $self = shift;
140 0           return scalar(@$self);
141             }
142              
143             sub iterator {
144 0     0 1   my $self = shift;
145 0           my $i = 0;
146             return sub {
147 0 0   0     return unless $self;
148              
149 0           my $frame = $self->[$i++];
150 0 0         unless ($frame) {
151 0           undef($self);
152             }
153 0           return $frame;
154 0           };
155             }
156              
157             sub frame {
158 0     0 1   my($self, $i) = @_;
159 0 0         return ($i < @$self) ? $self->[$i] : ();
160             }
161              
162             sub frames {
163 0     0 1   my $self = shift;
164 0           return @$self;
165             }
166              
167             sub as_string {
168 0     0 1   my $self = shift;
169 0           my $string = '';
170 0           for (my $iter = $self->iterator; my $frame = $iter->(); ) {
171 0           $string .= $frame->as_string . "\n";
172             }
173 0           return $string;
174             }
175              
176              
177             package Devel::Chitin::StackFrame;
178              
179             sub _new {
180 0     0     my($class, %params) = @_;
181 0           return bless \%params, $class;
182             }
183              
184             # Accessors
185             BEGIN {
186 34     34   327 no strict 'refs';
  34         77  
  34         2885  
187 34     34   173 foreach my $acc ( qw(package filename line subroutine hasargs wantarray
188             evaltext is_require hints bitmask
189             subname autoload level evalfile evalline serial callsite ) ) {
190 578     0   2266 *{$acc} = sub { return shift->{$acc} };
  578         6549  
  0            
191             }
192             }
193              
194             sub args {
195 0     0     my $args = shift->{args};
196 0           return @$args;
197             }
198              
199             sub as_string {
200 0     0     my $self = shift;
201 0           return sprintf('%s at %s:%d', map { $self->$_ } qw(subroutine filename line));
  0            
202             }
203              
204             1;
205              
206             __END__