File Coverage

blib/lib/Error/Show.pm
Criterion Covered Total %
statement 246 345 71.3
branch 54 110 49.0
condition 38 90 42.2
subroutine 16 18 88.8
pod 4 6 66.6
total 358 569 62.9


line stmt bran cond sub pod time code
1             package Error::Show;
2              
3 4     4   438726 use 5.024000;
  4         14  
4 4     4   18 use strict;
  4         9  
  4         88  
5 4     4   14 use warnings;
  4         16  
  4         243  
6 4     4   26 use feature "say";
  4         9  
  4         621  
7              
8              
9              
10             our $VERSION = 'v0.5.1';
11              
12 4     4   1732 use constant::more DEBUG=>undef;
  4         3657  
  4         25  
13             use constant::more {
14 4         75 PACKAGE=> 0,
15             FILENAME=> 1,
16             LINE=> 2,
17             SUBROUTINE=> 3,
18             HASARGS=> 4,
19             WANTARRAY=> 5,
20             EVALTEXT=> 6,
21             IS_REQUIRE=> 7,
22             HINTS=> 8,
23             BITMASK=> 9,
24             HINT_HASH=> 10,
25             MESSAGE=> 11,
26             SEQUENCE=> 12,
27             CODE_LINES=> 13,
28 4     4   621 };
  4         7  
29              
30             #
31             # A list of top level file paths or scalar refs to check for syntax errors
32             #
33             my @IINC;
34             sub context;
35              
36             my %programs;
37            
38             sub import {
39 4     4   30 my $package=shift;
40             # Add support for reexporters that manipulate the export level
41 4   50     51 my @caller=caller($Exporter::ExportLevel//0);;
42 4         9 my @options=@_;
43              
44              
45             # Only have one sub to export and we only export it if the caller has a line
46             # number. Otherise we are being invoked from the CLI
47             #
48 4 50       14 if($caller[LINE]){
49 4     4   3634 no strict "refs";
  4         7  
  4         18115  
50 4         5 my $name;
51 4         8 $name=$caller[0]."::context";
52 4         6 *{$name}=\&{"context"};
  4         16  
  4         13  
53              
54 4         10 $name=$caller[0]."::streval";
55 4         13 *{$name}=\&{"streval"};
  4         38  
  4         10  
56              
57 4         15 $name=$caller[0]."::throw";
58 4         7 *{$name}=\&{"throw"};
  4         14  
  4         10  
59 4         3345 return;
60             }
61              
62             #
63             # CLI Options include
64             #
65              
66 0         0 require POSIX; #For _exit;
67 0         0 require IPC::Open3;
68 0         0 require Symbol;
69 0         0 my %options;
70              
71 0         0 my $clean=grep /clean/i, @options;
72 0         0 my $splain=grep /splain/i, @options;
73 0         0 my $do_warn=grep /warn/i, @options;
74 0         0 my $no_handler=grep /no_handler/i, @options;
75              
76 0 0       0 my @warn=$do_warn?():"-MError::Show::Internal";
77              
78              
79             #
80             # 1. Command line argument activation ie -MError::Show
81             #
82             # Find out any extra lib paths used. To do this we:
83             #
84             # a. fork/exec a new perl process using the value of $^X.
85             # b. The new process dumps the @INC array to STDOUT
86             # c. This process reads the output and stores in @IINC
87             #
88             # Only run it the first time its used
89             # Is this the best way? Not sure. At least this way there is no argument
90             # processing, perl process does it for us.
91             #
92            
93 0 0       0 @IINC=map {chomp; $_} do {
  0         0  
  0         0  
94 0 0       0 open my $fh, "-|", $^X . q| -E 'map print("$_\n"), @INC'| or die "$!";
95 0         0 <$fh>;
96             } unless @IINC;
97              
98             #
99             # 2. Extract the extra include paths
100             #
101             # Built up the 'extra' array of any include paths not already listed
102             # from the STDOUT dumping above
103             #
104 0         0 my @extra=map {("-I", $_)} grep {my $i=$_; !grep { $i eq $_} @IINC} @INC;
  0         0  
  0         0  
  0         0  
  0         0  
105              
106              
107              
108             #
109             # 3. Syntax checking the program
110             #
111             # Now we have the include paths sorted,
112             # a. fork/exec again, this time with the -c switch for perl to check syntax
113             # b. slurp STDERR from child process
114             # c. execute the context routine to parse and show more source code context
115             # d. print!
116             # The proc
117              
118 0         0 local $/=undef;
119 0         0 my $file=$0;
120              
121             #push @file, @ARGV;
122              
123             #my $runnable=not $^C;#$options{check};
124             #for my $file(@file){
125 0 0 0     0 die "Error::Show cannot process STDIN, -e and -E programs" if $file eq "-e" or $file eq "-E" or $file eq "-";
      0        
126 0 0       0 die "Error::Show cannot access \"$file\"" unless -f $file;
127 0         0 my @cmd= ($^X ,@warn, @extra, "-c", $file);
128              
129 0         0 my $pid;
130             my $result;
131 0         0 eval {
132 0         0 $pid=IPC::Open3::open3(my $chld_in, my $chld_out, my $chld_err = Symbol::gensym(), @cmd);
133 0         0 $result=<$chld_err>;
134 0         0 close $chld_in;
135 0         0 close $chld_out;
136 0         0 close $chld_err;
137 0         0 wait;
138             };
139 0 0 0     0 if(!$pid and $@){
140 0         0 die "Error::Show failed to syntax check";
141             }
142              
143              
144             #
145             # 4. Status code from child indicates success
146             # When 0 this means syntax was ok. Otherwise error
147             # Attempt to propogate code to exit status
148             #
149 0 0       0 my $code=$?>255? (0xFF & ~$?): $?;
150              
151 0         0 my $runnable=$?==0;
152              
153 0         0 my $status=context( $result, splain=>$splain, clean=>$clean)."\n";
154              
155 0 0       0 if($^C){
156 0 0       0 if($runnable){
157             #only print status if we want warnings
158 0 0       0 print STDERR $do_warn?$status: "$file syntax OK\n";
159              
160             }
161             else{
162             #Not runnable, thus syntax error. Always print
163 0         0 print STDERR $status;
164              
165             }
166 0         0 POSIX::_exit $code;
167              
168             }
169             else{
170             #not checking, we want to run
171 0 0       0 if($runnable){
172             # don't bother with warnings
173              
174             # v0.4.0
175             # Install an global handler, unless asked not to
176             #
177 0 0       0 unless($no_handler){
178             $SIG{__DIE__}=sub {
179             # propagate eval and parsing errors
180 0 0 0 0   0 die @_ if $^S or ! defined $^S;
181              
182             # Otherwise hard error
183 0         0 my @frames;
184 0         0 my $i=0;
185 0         0 push @frames , [caller $i++] while caller $i;
186 0         0 say STDERR Error::Show::context bless {error=>$_[0], frames=>\@frames}, "Error::Show::Exception";
187 0         0 exit;
188 0         0 };
189             }
190              
191              
192             }
193             else{
194             #Not runnable, thus syntax error. Always print
195 0         0 print STDERR $status;
196 0         0 POSIX::_exit $code;
197             }
198             }
199             }
200              
201              
202             sub process_string_error{
203              
204 8     8 0 13 my $error_in=shift;
205 8         53 my %opts=@_;
206              
207 8         54 require Scalar::Util;
208 8         53 my @error_lines;
209             my @errors;
210             #my @entry;
211 8         0 my %entry;
212              
213            
214              
215             # Convert the object (or string) error to string.. and process the line numbers etc.
216             # This is the easiest way to support multiple Execption types.
217             #
218 8         0 my $error;
219 8   33     54 $error||="$error_in";
220              
221             #local $_=$error;
222             #Substitue with a line number relative to the start marker
223             #Reported line numbers are 1 based, stored lines are 0 based
224             #my $translation=$opts{translation};
225             #my $start=$opts{start};
226              
227 8         15 my $i=0;
228 8         30 for(split "\n", $error){
229 9         10 DEBUG and say STDERR "ERROR LINE: ".$_;
230 9 50 33     81 if(/at (.*?) line (\d+)/
231             or /Missing right curly or square bracket at (.*?) (\d+) at end of line/){
232             #
233             # Group by file names
234             #
235 9         15 DEBUG and say STDERR "PROCESSING: ".$_;
236 9         20 DEBUG and say STDERR "file: $1 and line $2";
237 9   50     61 my $entry=$entry{$1}//=[];
238             #push @$entry, {file=>$1, line=>$2,message=>$_, sequence=>$i++};
239 9         15 my $a=[];
240 9         24 $a->[FILENAME]=$1;
241 9         29 $a->[LINE]=$2-1;
242 9         20 $a->[MESSAGE]=$_;
243 9 50       22 $a->[MESSAGE]=$opts{message} if $opts{message};
244 9         20 $a->[SEQUENCE]=$i++;
245 9 50       43 $a->[EVALTEXT]=$opts{program} if $opts{program};
246 9         25 push @$entry, $a;
247             }
248             }
249              
250              
251              
252             #Key is file name
253             # value is a hash of filename,line number, perl error string and the sequence number
254              
255 8         34 \%entry;
256              
257             }
258              
259             # Takes a hash ref 'normalized error' sources, cross reference with source
260             # files and internal caching of string eval, and generates context lines around
261             # target line number
262              
263             sub text_output {
264 24     24 0 43 my $info_ref=shift;
265 24         136 my %opts=@_;
266 24         44 my $total="";
267 24         31 DEBUG and say STDERR "Reverse flag in text output set to: $opts{reverse}";
268              
269             # Sort by sequence number
270             # Errors are stored by filename internally. Sort by sequence number.
271             #
272              
273             my @sorted_info=
274 1         5 sort {$a->[SEQUENCE] <=> $b->[SEQUENCE] }
275 24         58 map { $_->@* } values %$info_ref;
  25         79  
276              
277             # Reverse the order if we want the first error listed last
278             #
279 24 100       78 @sorted_info=reverse (@sorted_info) if $opts{reverse};
280              
281             # Process each of the errors in sequence
282 24         35 my $counter=0;
283 24   50     93 my $limit=$opts{limit}//100;
284 24         45 for my $info (@sorted_info){
285 25 50 33     58 last if $counter>=$limit and $limit >0;
286 25         33 $counter++;
287 25 50       59 unless(exists $info->[CODE_LINES]){
288 25         34 my @code;
289            
290 25 100       160 if(my @f=$info->[FILENAME] =~ /\(eval \d+\)/g){
291             # Not actually a file, this was an eval
292 4         9 my $prog=$programs{$f[0]};
293 4   50     21 @code=split "\n", $prog//"";
294              
295             # Remove the cached code once its been accessed, unless we really want to keep it
296 4 100       11 delete $programs{$f[0]} unless $opts{keep};
297             }
298             else {
299 21         28 @code=split "\n", do {
300 21 50       6125 open my $fh, "<", $info->[FILENAME] or warn "Could not open file for reading: $info->[FILENAME]";
301 21         138 local $/=undef;
302 21         1267 <$fh>;
303             };
304             }
305 25         102 $info->[CODE_LINES]=\@code;
306             }
307              
308             # At this point we have lines of code in an array
309             #
310            
311             #Find start mark and end mark
312             #
313 25         38 my $start_line=0;
314 25 100       62 if($opts{start_mark}){
315 4         7 my $counter=0;
316 4         10 my $start_mark=$opts{start_mark};
317 4         11 for($info->[CODE_LINES]->@*){
318 22 100       126 if(/$start_mark/){
319 3         8 $start_line+=$counter+1;
320 3         7 last;
321             }
322 19         75 $counter++;
323             }
324             # Don't include the start marker in the results
325             }
326              
327 25         54 my $end_line=$info->[CODE_LINES]->@*-1;
328              
329 25 100       68 if($opts{end_mark}){
330 4         5 my $counter=0;
331 4         9 my $end_mark=$opts{end_mark};
332 4         11 for (reverse($info->[CODE_LINES]->@*)){
333 15 100       52 if(/$end_mark/){
334 3         6 $end_line-=$counter;
335 3         4 last;
336             }
337 12         17 $counter++;
338             }
339             }
340              
341 25 50       50 $start_line+=$opts{start_offset} if $opts{start_offset};
342 25 50       78 $end_line-=$opts{end_offset } if $opts{end_offset};
343              
344             # preclamp the error line to within this range so that 'Unmatched ' errors
345             # at least show ssomething.
346             #
347 25 50       57 $info->[LINE]=$end_line if $info->[LINE]>$end_line;
348              
349 25         33 DEBUG and say "START LINE after offset: $start_line";
350 25         29 DEBUG and say "END LINE after offset: $end_line";
351             # At this point the file min and max lines we should consider are
352             # start_line and end line inclusive. The $start_line is also used as an
353             # offset to shift error sources
354             #
355              
356 25         48 my $min=$info->[LINE]-$opts{pre_lines};
357 25         51 my $max=$info->[LINE]+$opts{post_lines};
358              
359 25         45 my $target= $info->[LINE];#-$start_line;
360 25         31 DEBUG and say "TARGET: $target";
361              
362 25 100       116 $min=$min<$start_line ? $start_line: $min;
363              
364 25 100       71 $max=$max>$end_line?$end_line:$max;
365              
366             #
367             # format counter on the largest number to be expected
368             #
369 25         60 my $f_len=length("$max");
370              
371 25   50     65 my $indent=$opts{current_indent}//"";
372 25         107 my $out="$indent$info->[FILENAME]\n";
373 25         60 $out.="\n";
374            
375 25         46 my $format="$indent%${f_len}d% 2s %s\n";
376 25         67 my $mark="";
377              
378             #Change min and max to one based index
379             #$min++;
380             #$max--;
381 25         29 DEBUG and say STDERR "min before print $min";
382 25         43 DEBUG and say STDERR "max before print $max";
383 25         79 for my $l($min..$max){
384 240         294 $mark="";
385              
386 240         333 my $a=$l-$start_line+1;
387              
388             #Perl line number is 1 based
389 240 100       375 $mark="=>" if $l==$target;
390              
391              
392             # Print lines as per the index in file array
393 240         599 $out.=sprintf $format, $a, $mark, $info->[CODE_LINES][$l];
394             }
395              
396 25         74 $total.=$out;
397            
398             # Modifiy the message now with updated line numbers
399             # TODO: Tidy this up
400 25 50       109 $info->[MESSAGE]=~s/line (\d+)(?:\.|,)/(($1-1)>$max?$max:$1-1)-$start_line+1/e;
  6         50  
401              
402 25         39 $total.="\n";
403 25 100       82 $total.=$info->[MESSAGE]."\n" unless $opts{clean};
404 25         54 $total.="\n";
405              
406             }
407 24 50       51 if($opts{splain}){
408 0         0 $total=splain($total);
409             }
410 24         174 $total;
411             }
412              
413              
414             #
415             # Front end to the main processing sub. Configures and checks the inputs
416             #
417             my $msg= "Trace must be a ref to array of {file=>.., line=>..} pairs";
418             sub context{
419 10 50 33 10 1 243445 shift if(defined $_[0] and $_[0] eq __PACKAGE__);
420              
421              
422 10         20 my $error=shift;
423 10 100       23 return unless $error;
424              
425 8         28 my %opts=@_;
426              
427 8         11 my $out;
428 8         12 my $do_internal_frames=1;
429              
430             #return unless $opts{error} or $opts{frames} or $do_internal_frames;
431             #$opts{start_mark};#//=qr|.*|; #regex which matches the start of the code
432 8   50     41 $opts{pre_lines}//=5; #Number of lines to show before target line
433 8   50     34 $opts{post_lines}//=5; #Number of lines to show after target line
434 8   50     34 $opts{start_offset}//=0; #Offset past start mark to consider as min line
435 8   50     41 $opts{end_offset}//=0; #Offset before end to consider as max line
436 8   50     38 $opts{translation}//=0; #A static value added to the line numbering
437 8   50     32 $opts{indent}//=" ";
438 8   50     79 $opts{file}//="";
439 8         16 $opts{current_indent}="";
440              
441              
442 8         13 my $depth=$opts{depth};
443              
444 8 100       23 unless($opts{reverse}){
445             # Show the actual error
446 7         34 $opts{clean}=undef;
447 7         35 my $info_ref=process_string_error $error, %opts ;
448 7         37 $out.=text_output $info_ref, %opts;
449 7         54 $opts{current_indent}.=$opts{indent};
450             }
451              
452              
453              
454              
455            
456             # Convert from supported exceptions classes to internal format
457 8         15 my $frames;
458 8   66     30 $frames||=eval {$error->{frames}}; # Error::Show::Exception
  8         44  
459 8   66     22 $frames||=eval {[$error->trace->frames]}; # Exception::Class::Base ok
  3         133  
460 8   66     20 $frames||=eval {$error->caller_stack}; # Exception::Base ok
  3         92  
461 8   66     19 $frames||=eval {[$error->getStackTrace]}; # Class::Throwable ok
  3         113  
462 8   66     20 $frames||=eval {\($error->frames)}; # Mojo::Exception ok
  3         98  
463 8   100     33 $frames||=[];
464              
465             #
466 8 100 66     54 if($do_internal_frames and @$frames==0){
467 3         5 my $i=1;
468              
469             #build call frames
470 3         4 my @frame;
471             my @stack;
472              
473 3         12 while(@frame=caller($i++)){
474 0         0 push @$frames, [@frame];
475             }
476             }
477              
478            
479 8         13 my $dstf="Devel::StackTrace::Frame";
480              
481 8         51 require Scalar::Util;
482              
483              
484             #DEBUG and ;
485              
486              
487             #If no depth, 0 or negative or clamp
488 8 0 33     33 if(!defined $depth or $depth<0 or $depth > @$frames){
      33        
489 8         13 $depth=@$frames;
490             }
491              
492             # Reverse the ordering of errors here if requested
493             #
494 8         26 my @frames_copy = @$frames;
495              
496 8         15 splice @frames_copy, $depth;
497            
498 8 100       24 @frames_copy=reverse @frames_copy if $opts{reverse};
499             # Check for trace kv pair. If this is present. We ignore the error
500             #
501             # Iterate through the list
502              
503             #my %_opts=%opts;
504 8         26 $opts{clean}=1;
505 8         43 my $i=0; #Sequence number
506 8         15 for my $e (@frames_copy) {
507              
508 16         33 my $a=[];
509 16 50 50     79 if((Scalar::Util::blessed($e)//"") eq "Devel::StackTrace::Frame"){
510             #Convert to an array
511 0         0 $a->[PACKAGE]=$e->package;
512 0         0 $a->[FILENAME]=$e->filename;
513 0         0 $a->[LINE]=$e->line;
514 0         0 $a->[SUBROUTINE]=$e->subroutine;
515 0         0 $a->[HASARGS]=$e->hasargs;
516 0         0 $a->[WANTARRAY]=$e->wantarray;
517 0         0 $a->[EVALTEXT]=$e->evaltext;
518 0         0 $a->[IS_REQUIRE]=$e->is_require;
519 0         0 $a->[HINTS]=$e->hints;
520 0         0 $a->[BITMASK]=$e->bitmask;
521 0         0 $a->[HINT_HASH]=$e->hints;
522             #$e=\@a;
523             }
524             else {
525             #Copy incase multiple calls to context on same error
526 16         84 @$a=$e->@*;
527             }
528              
529             # Skip over any frames from this package
530 16 50       89 next if $a->[PACKAGE] eq __PACKAGE__;
531              
532              
533 16   50     75 $a->[MESSAGE]//="";
534              
535             #Force a message if one is provided
536 16         25 $a->[LINE]--; #Make the error 0 based
537 16 50       52 $a->[MESSAGE]=$opts{message} if $opts{message};
538 16         29 $a->[SEQUENCE]=$i++;
539              
540             # Generate the context here
541             #
542 16         50 my %entry;
543 16         79 my $entry=$entry{$a->[FILENAME]}=[];
544 16         34 push @$entry, $a;
545 16         95 $out.= text_output \%entry, %opts;
546 16         152 $opts{current_indent}.=$opts{indent};
547             }
548 8 100       24 if($opts{reverse}){
549             # Show the actual error
550 1         3 $opts{clean}=undef;
551 1         6 my $info_ref=process_string_error $error, %opts ;
552 1         6 $out.=text_output $info_ref, %opts;
553 1         12 $opts{current_indent}.=$opts{indent};
554             }
555 8         65 $out;
556             }
557              
558              
559              
560             my ($chld_in, $chld_out, $chld_err);
561             my @cmd="splain";
562             my $pid;
563              
564             sub splain {
565 0     0 1 0 my $out;
566             #Attempt to open splain process if it isn't already
567 0 0       0 unless($pid){
568 0         0 eval{
569 0         0 $pid= IPC::Open3::open3($chld_in, $chld_out, $chld_err = Symbol::gensym(), @cmd);
570             #$chld_in->autoflush(1);
571              
572             };
573 0 0 0     0 if(!$pid and $@){
574 0         0 warn "Error::Show Could not splain the results";
575             }
576             };
577              
578             #Attempt to write to the process and read from it
579 0         0 eval {
580 0         0 print $chld_in $_[0], "\n";;
581 0         0 close $chld_in;
582 0         0 $out=<$chld_out>;
583 0         0 close $chld_out;
584 0         0 close $chld_err;
585             };
586              
587 0 0       0 if($@){
588 0         0 $pid=undef;
589 0         0 close $chld_in;
590 0         0 close $chld_out;
591 0         0 close $chld_err;
592 0         0 warn "Error::Show Could not splain the results";
593             }
594 0         0 $out;
595             }
596              
597             sub streval ($;$){
598              
599             # The program we want to execute
600 2     2 1 125418 my $code= $_[0];
601 2 50       8 if(ref($code) eq "CODE"){
602 0         0 return eval {$code->()};
  0         0  
603             }
604 2   33     12 my $package=$_[1]//caller;
605              
606              
607             # Wrap the eval in a sub. Here we can seperate syntax/complile errors and run
608             # time errors
609             #
610              
611 2         4 my $file;
612              
613             # Do eval to get current eval number and then calculate the NEXT eval number
614 2         341 my $number=eval '__FILE__=~ qr/(\d+)/; $1';
615 2         11 $number++;
616 2         5 $file="(eval $number)";
617 2         4 $programs{$file}=$code;
618 2         4 my @in_sub_frame;
619             # Attempt to compile
620             #
621             my $sub;
622             {
623 2         2 local $@;
  2         3  
624             #$sub=eval "sub {package $package; \@in_sub_frame=caller(0); local \$@; my \@res=eval {$code}; if(\$@){} \@res}";
625 2         407 $sub=eval "sub {package $package; \@in_sub_frame=caller(0); $code}";
626              
627             # Check for SYNTAX error
628             #
629 2         15 my $error=$@;
630 2 100 66     12 if(!defined($sub) or $error){
631 1 50       3 if(!ref $error){
632             # extract the filename (including the () )stored in the error
633 1         7 my $filename= $error=~/\(eval \d+\)/g;
634              
635 1         2 my @frame;
636             my @stack;
637              
638 1         1 my $i=1;
639 1         2 push @stack, [@frame]; #frame from actual eval
640 1         10 while(@frame=caller($i++)){
641 1         4 push @stack, [@frame];
642             }
643              
644 1         7 my $o=bless {error=>$error, frames=>\@stack}, "Error::Show::Exception";
645 1         7 die $o;#{error=>$error, frames=>\@stack};
646             }
647             else{
648 0         0 die $error;
649             }
650             }
651             }
652              
653              
654 1         2 my $result;
655             {
656             # Check for RUNTIME error
657 1         2 local $@;
  1         1  
658 1         1 my @frame;
659 1         2 $result=eval { $sub->(); };
  1         18  
660 1         3 my $error=$@;
661 1 50       3 if($error){
662 0 0       0 if(!ref $error){
663             # extract the filename stored in the error string
664 0         0 my $filename= $error=~/\(eval (\d+)\)/g;
665 0         0 my @stack;
666 0         0 my $i=1;
667 0         0 push @stack, [@in_sub_frame]; #frame from actual eval
668 0         0 while(@frame=caller($i++)){
669 0         0 push @stack, [@frame];
670             }
671              
672 0         0 my $o=bless {error=>$error, frames=>\@stack}, "Error::Show::Exception";
673 0         0 die $o;
674             }
675             else {
676             # Rethrow as is
677 0         0 die $error;
678             }
679             }
680             }
681              
682             # otherwise return the result
683 1         2 $result;
684             }
685              
686             sub throw {
687 2     2 1 294229 my $error=shift;
688 2   33     11 $error//=$@;
689 2         17 my @c=caller(0);
690            
691 2         6 my @frames;
692 2         4 my $i=1;
693 2         13 while(my @frame=caller($i++)){
694 8         79 push @frames, \@frame;
695             }
696              
697 2 50       8 unless(ref $error){
698             # Error is just a string. so we re create the file and line number
699             # from the the caller this sub
700             #
701 2         45 die bless {error=>"$error at $c[1] line $c[2]", frames=>\@frames}, "Error::Show::Exception";
702             }
703             else {
704             # rethrow
705 0         0 die bless {error=>$error, frames=>\@frames}, "Error::Show::Exception";
706             }
707             }
708              
709             package Error::Show::Exception;
710             use overload
711 20     20   1202 '""'=>sub { "$_[0]{error}" },
712 4     4   2340 'eq'=>sub { "$_[0]{error}" eq $_[1] };
  4     5   6188  
  4         49  
  5         32  
713              
714             1;
715             __END__