File Coverage

blib/lib/Devel/StackTrace.pm
Criterion Covered Total %
statement 131 131 100.0
branch 47 54 87.0
condition 15 20 75.0
subroutine 21 21 100.0
pod 8 8 100.0
total 222 234 94.8


line stmt bran cond sub pod time code
1             package Devel::StackTrace;
2              
3 10     10   186637 use 5.006;
  10         51  
4              
5 10     10   61 use strict;
  10         16  
  10         260  
6 10     10   42 use warnings;
  10         16  
  10         590  
7              
8             our $VERSION = '2.02';
9              
10 10     10   4709 use Devel::StackTrace::Frame;
  10         104  
  10         322  
11 10     10   76 use File::Spec;
  10         22  
  10         306  
12 10     10   82 use Scalar::Util qw( blessed );
  10         12  
  10         1551  
13              
14             use overload
15 10         106 '""' => \&as_string,
16 10     10   13645 fallback => 1;
  10         11058  
17              
18             sub new {
19 30     30 1 11622 my $class = shift;
20 30         121 my %p = @_;
21              
22             $p{unsafe_ref_capture} = !delete $p{no_refs}
23 30 100       105 if exists $p{no_refs};
24              
25 30         149 my $self = bless {
26             index => undef,
27             frames => [],
28             raw => [],
29             %p,
30             }, $class;
31              
32 30         74 $self->_record_caller_data;
33              
34 30         108 return $self;
35             }
36              
37             sub _record_caller_data {
38 30     30   37 my $self = shift;
39              
40 30   66     324 my $filter = $self->{filter_frames_early} && $self->_make_frame_filter;
41              
42             # We exclude this method by starting at least one frame back.
43 30   100     176 my $x = 1 + ( $self->{skip_frames} || 0 );
44              
45 30 100       83 while (
46             my @c
47             = $self->{no_args}
48             ? caller( $x++ )
49             : do {
50             ## no critic (Modules::ProhibitMultiplePackages, Variables::ProhibitPackageVars)
51             package # the newline keeps dzil from adding a version here
52             DB;
53 105         125 @DB::args = ();
54 105         718 caller( $x++ );
55             }
56             ) {
57              
58 79         65 my @args;
59              
60             ## no critic (Variables::ProhibitPackageVars)
61 79 100       174 @args = $self->{no_args} ? () : @DB::args;
62             ## use critic
63              
64 79         164 my $raw = {
65             caller => \@c,
66             args => \@args,
67             };
68              
69 79 50 66     171 next if $filter && !$filter->($raw);
70              
71 79 100       1158 unless ( $self->{unsafe_ref_capture} ) {
72 99 100       242 $raw->{args} = [ map { ref $_ ? $self->_ref_to_string($_) : $_ }
73 73         65 @{ $raw->{args} } ];
  73         123  
74             }
75              
76 79         104 push @{ $self->{raw} }, $raw;
  79         250  
77             }
78             }
79              
80             sub _ref_to_string {
81 12     12   13 my $self = shift;
82 12         10 my $ref = shift;
83              
84 12 50 66     140 return overload::AddrRef($ref)
85             if blessed $ref && $ref->isa('Exception::Class::Base');
86              
87 12 100       44 return overload::AddrRef($ref) unless $self->{respect_overload};
88              
89             ## no critic (Variables::RequireInitializationForLocalVars)
90 2         3 local $@;
91 2         5 local $SIG{__DIE__};
92             ## use critic
93              
94 2         3 my $str = eval { $ref . q{} };
  2         35  
95              
96 2 100       13 return $@ ? overload::AddrRef($ref) : $str;
97             }
98              
99             sub _make_frames {
100 28     28   26 my $self = shift;
101              
102 28   66     111 my $filter = !$self->{filter_frames_early} && $self->_make_frame_filter;
103              
104 28         45 my $raw = delete $self->{raw};
105 28         34 for my $r ( @{$raw} ) {
  28         57  
106 74 100 100     183 next if $filter && !$filter->($r);
107              
108 67         139 $self->_add_frame( $r->{caller}, $r->{args} );
109             }
110             }
111              
112             my $default_filter = sub {1};
113              
114             sub _make_frame_filter {
115 28     28   47 my $self = shift;
116              
117 28         23 my ( @i_pack_re, %i_class );
118 28 100       68 if ( $self->{ignore_package} ) {
119             ## no critic (Variables::RequireInitializationForLocalVars)
120 2         3 local $@;
121 2         7 local $SIG{__DIE__};
122             ## use critic
123              
124             $self->{ignore_package} = [ $self->{ignore_package} ]
125 2 50       3 unless eval { @{ $self->{ignore_package} } };
  2         3  
  2         23  
126              
127             @i_pack_re
128 2 100       4 = map { ref $_ ? $_ : qr/^\Q$_\E$/ } @{ $self->{ignore_package} };
  2         24  
  2         4  
129             }
130              
131 28         35 my $p = __PACKAGE__;
132 28         326 push @i_pack_re, qr/^\Q$p\E$/;
133              
134 28 100       71 if ( $self->{ignore_class} ) {
135             $self->{ignore_class} = [ $self->{ignore_class} ]
136 2 50       8 unless ref $self->{ignore_class};
137 2         3 %i_class = map { $_ => 1 } @{ $self->{ignore_class} };
  2         7  
  2         4  
138             }
139              
140 28         37 my $user_filter = $self->{frame_filter};
141              
142             return sub {
143 74 100   74   86 return 0 if grep { $_[0]{caller}[0] =~ /$_/ } @i_pack_re;
  78         349  
144 72 100       197 return 0 if grep { $_[0]{caller}[0]->isa($_) } keys %i_class;
  6         51  
145              
146 68 100       110 if ($user_filter) {
147 5         13 return $user_filter->( $_[0] );
148             }
149              
150 63         185 return 1;
151 28         187 };
152             }
153              
154             sub _add_frame {
155 67     67   68 my $self = shift;
156 67         59 my $c = shift;
157 67         67 my $p = shift;
158              
159             # eval and is_require are only returned when applicable under 5.00503.
160 67 50       130 push @$c, ( undef, undef ) if scalar @$c == 6;
161              
162 67         407 push @{ $self->{frames} },
163             Devel::StackTrace::Frame->new(
164             $c,
165             $p,
166             $self->{respect_overload},
167             $self->{max_arg_length},
168             $self->{message},
169             $self->{indent}
170 67         63 );
171             }
172              
173             sub next_frame {
174 13     13 1 852 my $self = shift;
175              
176             # reset to top if necessary.
177 13 100       34 $self->{index} = -1 unless defined $self->{index};
178              
179 13         18 my @f = $self->frames;
180 13 100       29 if ( defined $f[ $self->{index} + 1 ] ) {
181 10         20 return $f[ ++$self->{index} ];
182             }
183             else {
184 3         7 $self->{index} = undef;
185             ## no critic (Subroutines::ProhibitExplicitReturnUndef)
186 3         6 return undef;
187             }
188             }
189              
190             sub prev_frame {
191 9     9 1 38 my $self = shift;
192              
193 9         16 my @f = $self->frames;
194              
195             # reset to top if necessary.
196 9 100       21 $self->{index} = scalar @f unless defined $self->{index};
197              
198 9 100 66     35 if ( defined $f[ $self->{index} - 1 ] && $self->{index} >= 1 ) {
199 6         13 return $f[ --$self->{index} ];
200             }
201             else {
202             ## no critic (Subroutines::ProhibitExplicitReturnUndef)
203 3         4 $self->{index} = undef;
204 3         4 return undef;
205             }
206             }
207              
208             sub reset_pointer {
209 1     1 1 28 my $self = shift;
210              
211 1         3 $self->{index} = undef;
212              
213 1         2 return;
214             }
215              
216             sub frames {
217 53     53 1 106 my $self = shift;
218              
219 53 100       103 if (@_) {
220             die
221             "Devel::StackTrace->frames can only take Devel::StackTrace::Frame args\n"
222 2 50       5 if grep { !$_->isa('Devel::StackTrace::Frame') } @_;
  3         21  
223              
224 2         6 $self->{frames} = \@_;
225 2         5 delete $self->{raw};
226             }
227             else {
228 51 100       142 $self->_make_frames if $self->{raw};
229             }
230              
231 53         124 return @{ $self->{frames} };
  53         127  
232             }
233              
234             sub frame {
235 2     2 1 2 my $self = shift;
236 2         4 my $i = shift;
237              
238 2 50       6 return unless defined $i;
239              
240 2         6 return ( $self->frames )[$i];
241             }
242              
243             sub frame_count {
244 1     1 1 5 my $self = shift;
245              
246 1         3 return scalar( $self->frames );
247             }
248              
249             sub as_string {
250 12     12 1 62 my $self = shift;
251 12         13 my $p = shift;
252              
253 12         13 my $st = q{};
254 12         10 my $first = 1;
255 12         30 foreach my $f ( $self->frames ) {
256 30         73 $st .= $f->as_string( $first, $p ) . "\n";
257 30         45 $first = 0;
258             }
259              
260 12         60 return $st;
261             }
262              
263             {
264             ## no critic (Modules::ProhibitMultiplePackages, ClassHierarchies::ProhibitExplicitISA)
265             package # hide from PAUSE
266             Devel::StackTraceFrame;
267              
268             our @ISA = 'Devel::StackTrace::Frame';
269             }
270              
271             1;
272              
273             # ABSTRACT: An object representing a stack trace
274              
275             __END__