Code

ebd07498a22ec598449211e8a26c0b63689633ff
[git.git] / perl / private-Error.pm
1 # Error.pm
2 #
3 # Copyright (c) 1997-8 Graham Barr <gbarr@ti.com>. All rights reserved.
4 # This program is free software; you can redistribute it and/or
5 # modify it under the same terms as Perl itself.
6 #
7 # Based on my original Error.pm, and Exceptions.pm by Peter Seibel
8 # <peter@weblogic.com> and adapted by Jesse Glick <jglick@sig.bsh.com>.
9 #
10 # but modified ***significantly***
12 package Error;
14 use strict;
15 use vars qw($VERSION);
16 use 5.004;
18 $VERSION = "0.15009";
20 use overload (
21         '""'       =>   'stringify',
22         '0+'       =>   'value',
23         'bool'     =>   sub { return 1; },
24         'fallback' =>   1
25 );
27 $Error::Depth = 0;      # Depth to pass to caller()
28 $Error::Debug = 0;      # Generate verbose stack traces
29 @Error::STACK = ();     # Clause stack for try
30 $Error::THROWN = undef; # last error thrown, a workaround until die $ref works
32 my $LAST;               # Last error created
33 my %ERROR;              # Last error associated with package
35 sub throw_Error_Simple
36 {
37     my $args = shift;
38     return Error::Simple->new($args->{'text'});
39 }
41 $Error::ObjectifyCallback = \&throw_Error_Simple;
44 # Exported subs are defined in Error::subs
46 use Scalar::Util ();
48 sub import {
49     shift;
50     local $Exporter::ExportLevel = $Exporter::ExportLevel + 1;
51     Error::subs->import(@_);
52 }
54 # I really want to use last for the name of this method, but it is a keyword
55 # which prevent the syntax  last Error
57 sub prior {
58     shift; # ignore
60     return $LAST unless @_;
62     my $pkg = shift;
63     return exists $ERROR{$pkg} ? $ERROR{$pkg} : undef
64         unless ref($pkg);
66     my $obj = $pkg;
67     my $err = undef;
68     if($obj->isa('HASH')) {
69         $err = $obj->{'__Error__'}
70             if exists $obj->{'__Error__'};
71     }
72     elsif($obj->isa('GLOB')) {
73         $err = ${*$obj}{'__Error__'}
74             if exists ${*$obj}{'__Error__'};
75     }
77     $err;
78 }
80 sub flush {
81     shift; #ignore
83     unless (@_) {
84        $LAST = undef;
85        return;
86     }
88     my $pkg = shift;
89     return unless ref($pkg);
91     undef $ERROR{$pkg} if defined $ERROR{$pkg};
92 }
94 # Return as much information as possible about where the error
95 # happened. The -stacktrace element only exists if $Error::DEBUG
96 # was set when the error was created
98 sub stacktrace {
99     my $self = shift;
101     return $self->{'-stacktrace'}
102         if exists $self->{'-stacktrace'};
104     my $text = exists $self->{'-text'} ? $self->{'-text'} : "Died";
106     $text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
107         unless($text =~ /\n$/s);
109     $text;
112 # Allow error propagation, ie
114 # $ber->encode(...) or
115 #    return Error->prior($ber)->associate($ldap);
117 sub associate {
118     my $err = shift;
119     my $obj = shift;
121     return unless ref($obj);
123     if($obj->isa('HASH')) {
124         $obj->{'__Error__'} = $err;
125     }
126     elsif($obj->isa('GLOB')) {
127         ${*$obj}{'__Error__'} = $err;
128     }
129     $obj = ref($obj);
130     $ERROR{ ref($obj) } = $err;
132     return;
135 sub new {
136     my $self = shift;
137     my($pkg,$file,$line) = caller($Error::Depth);
139     my $err = bless {
140         '-package' => $pkg,
141         '-file'    => $file,
142         '-line'    => $line,
143         @_
144     }, $self;
146     $err->associate($err->{'-object'})
147         if(exists $err->{'-object'});
149     # To always create a stacktrace would be very inefficient, so
150     # we only do it if $Error::Debug is set
152     if($Error::Debug) {
153         require Carp;
154         local $Carp::CarpLevel = $Error::Depth;
155         my $text = defined($err->{'-text'}) ? $err->{'-text'} : "Error";
156         my $trace = Carp::longmess($text);
157         # Remove try calls from the trace
158         $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
159         $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::run_clauses[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
160         $err->{'-stacktrace'} = $trace
161     }
163     $@ = $LAST = $ERROR{$pkg} = $err;
166 # Throw an error. this contains some very gory code.
168 sub throw {
169     my $self = shift;
170     local $Error::Depth = $Error::Depth + 1;
172     # if we are not rethrow-ing then create the object to throw
173     $self = $self->new(@_) unless ref($self);
175     die $Error::THROWN = $self;
178 # syntactic sugar for
180 #    die with Error( ... );
182 sub with {
183     my $self = shift;
184     local $Error::Depth = $Error::Depth + 1;
186     $self->new(@_);
189 # syntactic sugar for
191 #    record Error( ... ) and return;
193 sub record {
194     my $self = shift;
195     local $Error::Depth = $Error::Depth + 1;
197     $self->new(@_);
200 # catch clause for
202 # try { ... } catch CLASS with { ... }
204 sub catch {
205     my $pkg = shift;
206     my $code = shift;
207     my $clauses = shift || {};
208     my $catch = $clauses->{'catch'} ||= [];
210     unshift @$catch,  $pkg, $code;
212     $clauses;
215 # Object query methods
217 sub object {
218     my $self = shift;
219     exists $self->{'-object'} ? $self->{'-object'} : undef;
222 sub file {
223     my $self = shift;
224     exists $self->{'-file'} ? $self->{'-file'} : undef;
227 sub line {
228     my $self = shift;
229     exists $self->{'-line'} ? $self->{'-line'} : undef;
232 sub text {
233     my $self = shift;
234     exists $self->{'-text'} ? $self->{'-text'} : undef;
237 # overload methods
239 sub stringify {
240     my $self = shift;
241     defined $self->{'-text'} ? $self->{'-text'} : "Died";
244 sub value {
245     my $self = shift;
246     exists $self->{'-value'} ? $self->{'-value'} : undef;
249 package Error::Simple;
251 @Error::Simple::ISA = qw(Error);
253 sub new {
254     my $self  = shift;
255     my $text  = "" . shift;
256     my $value = shift;
257     my(@args) = ();
259     local $Error::Depth = $Error::Depth + 1;
261     @args = ( -file => $1, -line => $2)
262         if($text =~ s/\s+at\s+(\S+)\s+line\s+(\d+)(?:,\s*<[^>]*>\s+line\s+\d+)?\.?\n?$//s);
263     push(@args, '-value', 0 + $value)
264         if defined($value);
266     $self->SUPER::new(-text => $text, @args);
269 sub stringify {
270     my $self = shift;
271     my $text = $self->SUPER::stringify;
272     $text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
273         unless($text =~ /\n$/s);
274     $text;
277 ##########################################################################
278 ##########################################################################
280 # Inspired by code from Jesse Glick <jglick@sig.bsh.com> and
281 # Peter Seibel <peter@weblogic.com>
283 package Error::subs;
285 use Exporter ();
286 use vars qw(@EXPORT_OK @ISA %EXPORT_TAGS);
288 @EXPORT_OK   = qw(try with finally except otherwise);
289 %EXPORT_TAGS = (try => \@EXPORT_OK);
291 @ISA = qw(Exporter);
293 sub run_clauses ($$$\@) {
294     my($clauses,$err,$wantarray,$result) = @_;
295     my $code = undef;
297     $err = $Error::ObjectifyCallback->({'text' =>$err}) unless ref($err);
299     CATCH: {
301         # catch
302         my $catch;
303         if(defined($catch = $clauses->{'catch'})) {
304             my $i = 0;
306             CATCHLOOP:
307             for( ; $i < @$catch ; $i += 2) {
308                 my $pkg = $catch->[$i];
309                 unless(defined $pkg) {
310                     #except
311                     splice(@$catch,$i,2,$catch->[$i+1]->());
312                     $i -= 2;
313                     next CATCHLOOP;
314                 }
315                 elsif(Scalar::Util::blessed($err) && $err->isa($pkg)) {
316                     $code = $catch->[$i+1];
317                     while(1) {
318                         my $more = 0;
319                         local($Error::THROWN);
320                         my $ok = eval {
321                             if($wantarray) {
322                                 @{$result} = $code->($err,\$more);
323                             }
324                             elsif(defined($wantarray)) {
325                                 @{$result} = ();
326                                 $result->[0] = $code->($err,\$more);
327                             }
328                             else {
329                                 $code->($err,\$more);
330                             }
331                             1;
332                         };
333                         if( $ok ) {
334                             next CATCHLOOP if $more;
335                             undef $err;
336                         }
337                         else {
338                             $err = defined($Error::THROWN)
339                                     ? $Error::THROWN : $@;
340                 $err = $Error::ObjectifyCallback->({'text' =>$err})
341                     unless ref($err);
342                         }
343                         last CATCH;
344                     };
345                 }
346             }
347         }
349         # otherwise
350         my $owise;
351         if(defined($owise = $clauses->{'otherwise'})) {
352             my $code = $clauses->{'otherwise'};
353             my $more = 0;
354             my $ok = eval {
355                 if($wantarray) {
356                     @{$result} = $code->($err,\$more);
357                 }
358                 elsif(defined($wantarray)) {
359                     @{$result} = ();
360                     $result->[0] = $code->($err,\$more);
361                 }
362                 else {
363                     $code->($err,\$more);
364                 }
365                 1;
366             };
367             if( $ok ) {
368                 undef $err;
369             }
370             else {
371                 $err = defined($Error::THROWN)
372                         ? $Error::THROWN : $@;
374         $err = $Error::ObjectifyCallback->({'text' =>$err})
375             unless ref($err);
376             }
377         }
378     }
379     $err;
382 sub try (&;$) {
383     my $try = shift;
384     my $clauses = @_ ? shift : {};
385     my $ok = 0;
386     my $err = undef;
387     my @result = ();
389     unshift @Error::STACK, $clauses;
391     my $wantarray = wantarray();
393     do {
394         local $Error::THROWN = undef;
395     local $@ = undef;
397         $ok = eval {
398             if($wantarray) {
399                 @result = $try->();
400             }
401             elsif(defined $wantarray) {
402                 $result[0] = $try->();
403             }
404             else {
405                 $try->();
406             }
407             1;
408         };
410         $err = defined($Error::THROWN) ? $Error::THROWN : $@
411             unless $ok;
412     };
414     shift @Error::STACK;
416     $err = run_clauses($clauses,$err,wantarray,@result)
417         unless($ok);
419     $clauses->{'finally'}->()
420         if(defined($clauses->{'finally'}));
422     if (defined($err))
423     {
424         if (Scalar::Util::blessed($err) && $err->can('throw'))
425         {
426             throw $err;
427         }
428         else
429         {
430             die $err;
431         }
432     }
434     wantarray ? @result : $result[0];
437 # Each clause adds a sub to the list of clauses. The finally clause is
438 # always the last, and the otherwise clause is always added just before
439 # the finally clause.
441 # All clauses, except the finally clause, add a sub which takes one argument
442 # this argument will be the error being thrown. The sub will return a code ref
443 # if that clause can handle that error, otherwise undef is returned.
445 # The otherwise clause adds a sub which unconditionally returns the users
446 # code reference, this is why it is forced to be last.
448 # The catch clause is defined in Error.pm, as the syntax causes it to
449 # be called as a method
451 sub with (&;$) {
452     @_
455 sub finally (&) {
456     my $code = shift;
457     my $clauses = { 'finally' => $code };
458     $clauses;
461 # The except clause is a block which returns a hashref or a list of
462 # key-value pairs, where the keys are the classes and the values are subs.
464 sub except (&;$) {
465     my $code = shift;
466     my $clauses = shift || {};
467     my $catch = $clauses->{'catch'} ||= [];
469     my $sub = sub {
470         my $ref;
471         my(@array) = $code->($_[0]);
472         if(@array == 1 && ref($array[0])) {
473             $ref = $array[0];
474             $ref = [ %$ref ]
475                 if(UNIVERSAL::isa($ref,'HASH'));
476         }
477         else {
478             $ref = \@array;
479         }
480         @$ref
481     };
483     unshift @{$catch}, undef, $sub;
485     $clauses;
488 sub otherwise (&;$) {
489     my $code = shift;
490     my $clauses = shift || {};
492     if(exists $clauses->{'otherwise'}) {
493         require Carp;
494         Carp::croak("Multiple otherwise clauses");
495     }
497     $clauses->{'otherwise'} = $code;
499     $clauses;
502 1;
503 __END__
505 =head1 NAME
507 Error - Error/exception handling in an OO-ish way
509 =head1 SYNOPSIS
511     use Error qw(:try);
513     throw Error::Simple( "A simple error");
515     sub xyz {
516         ...
517         record Error::Simple("A simple error")
518             and return;
519     }
521     unlink($file) or throw Error::Simple("$file: $!",$!);
523     try {
524         do_some_stuff();
525         die "error!" if $condition;
526         throw Error::Simple -text => "Oops!" if $other_condition;
527     }
528     catch Error::IO with {
529         my $E = shift;
530         print STDERR "File ", $E->{'-file'}, " had a problem\n";
531     }
532     except {
533         my $E = shift;
534         my $general_handler=sub {send_message $E->{-description}};
535         return {
536             UserException1 => $general_handler,
537             UserException2 => $general_handler
538         };
539     }
540     otherwise {
541         print STDERR "Well I don't know what to say\n";
542     }
543     finally {
544         close_the_garage_door_already(); # Should be reliable
545     }; # Don't forget the trailing ; or you might be surprised
547 =head1 DESCRIPTION
549 The C<Error> package provides two interfaces. Firstly C<Error> provides
550 a procedural interface to exception handling. Secondly C<Error> is a
551 base class for errors/exceptions that can either be thrown, for
552 subsequent catch, or can simply be recorded.
554 Errors in the class C<Error> should not be thrown directly, but the
555 user should throw errors from a sub-class of C<Error>.
557 =head1 PROCEDURAL INTERFACE
559 C<Error> exports subroutines to perform exception handling. These will
560 be exported if the C<:try> tag is used in the C<use> line.
562 =over 4
564 =item try BLOCK CLAUSES
566 C<try> is the main subroutine called by the user. All other subroutines
567 exported are clauses to the try subroutine.
569 The BLOCK will be evaluated and, if no error is throw, try will return
570 the result of the block.
572 C<CLAUSES> are the subroutines below, which describe what to do in the
573 event of an error being thrown within BLOCK.
575 =item catch CLASS with BLOCK
577 This clauses will cause all errors that satisfy C<$err-E<gt>isa(CLASS)>
578 to be caught and handled by evaluating C<BLOCK>.
580 C<BLOCK> will be passed two arguments. The first will be the error
581 being thrown. The second is a reference to a scalar variable. If this
582 variable is set by the catch block then, on return from the catch
583 block, try will continue processing as if the catch block was never
584 found.
586 To propagate the error the catch block may call C<$err-E<gt>throw>
588 If the scalar reference by the second argument is not set, and the
589 error is not thrown. Then the current try block will return with the
590 result from the catch block.
592 =item except BLOCK
594 When C<try> is looking for a handler, if an except clause is found
595 C<BLOCK> is evaluated. The return value from this block should be a
596 HASHREF or a list of key-value pairs, where the keys are class names
597 and the values are CODE references for the handler of errors of that
598 type.
600 =item otherwise BLOCK
602 Catch any error by executing the code in C<BLOCK>
604 When evaluated C<BLOCK> will be passed one argument, which will be the
605 error being processed.
607 Only one otherwise block may be specified per try block
609 =item finally BLOCK
611 Execute the code in C<BLOCK> either after the code in the try block has
612 successfully completed, or if the try block throws an error then
613 C<BLOCK> will be executed after the handler has completed.
615 If the handler throws an error then the error will be caught, the
616 finally block will be executed and the error will be re-thrown.
618 Only one finally block may be specified per try block
620 =back
622 =head1 CLASS INTERFACE
624 =head2 CONSTRUCTORS
626 The C<Error> object is implemented as a HASH. This HASH is initialized
627 with the arguments that are passed to it's constructor. The elements
628 that are used by, or are retrievable by the C<Error> class are listed
629 below, other classes may add to these.
631         -file
632         -line
633         -text
634         -value
635         -object
637 If C<-file> or C<-line> are not specified in the constructor arguments
638 then these will be initialized with the file name and line number where
639 the constructor was called from.
641 If the error is associated with an object then the object should be
642 passed as the C<-object> argument. This will allow the C<Error> package
643 to associate the error with the object.
645 The C<Error> package remembers the last error created, and also the
646 last error associated with a package. This could either be the last
647 error created by a sub in that package, or the last error which passed
648 an object blessed into that package as the C<-object> argument.
650 =over 4
652 =item throw ( [ ARGS ] )
654 Create a new C<Error> object and throw an error, which will be caught
655 by a surrounding C<try> block, if there is one. Otherwise it will cause
656 the program to exit.
658 C<throw> may also be called on an existing error to re-throw it.
660 =item with ( [ ARGS ] )
662 Create a new C<Error> object and returns it. This is defined for
663 syntactic sugar, eg
665     die with Some::Error ( ... );
667 =item record ( [ ARGS ] )
669 Create a new C<Error> object and returns it. This is defined for
670 syntactic sugar, eg
672     record Some::Error ( ... )
673         and return;
675 =back
677 =head2 STATIC METHODS
679 =over 4
681 =item prior ( [ PACKAGE ] )
683 Return the last error created, or the last error associated with
684 C<PACKAGE>
686 =item flush ( [ PACKAGE ] )
688 Flush the last error created, or the last error associated with
689 C<PACKAGE>.It is necessary to clear the error stack before exiting the
690 package or uncaught errors generated using C<record> will be reported.
692      $Error->flush;
694 =cut
696 =back
698 =head2 OBJECT METHODS
700 =over 4
702 =item stacktrace
704 If the variable C<$Error::Debug> was non-zero when the error was
705 created, then C<stacktrace> returns a string created by calling
706 C<Carp::longmess>. If the variable was zero the C<stacktrace> returns
707 the text of the error appended with the filename and line number of
708 where the error was created, providing the text does not end with a
709 newline.
711 =item object
713 The object this error was associated with
715 =item file
717 The file where the constructor of this error was called from
719 =item line
721 The line where the constructor of this error was called from
723 =item text
725 The text of the error
727 =back
729 =head2 OVERLOAD METHODS
731 =over 4
733 =item stringify
735 A method that converts the object into a string. This method may simply
736 return the same as the C<text> method, or it may append more
737 information. For example the file name and line number.
739 By default this method returns the C<-text> argument that was passed to
740 the constructor, or the string C<"Died"> if none was given.
742 =item value
744 A method that will return a value that can be associated with the
745 error. For example if an error was created due to the failure of a
746 system call, then this may return the numeric value of C<$!> at the
747 time.
749 By default this method returns the C<-value> argument that was passed
750 to the constructor.
752 =back
754 =head1 PRE-DEFINED ERROR CLASSES
756 =over 4
758 =item Error::Simple
760 This class can be used to hold simple error strings and values. It's
761 constructor takes two arguments. The first is a text value, the second
762 is a numeric value. These values are what will be returned by the
763 overload methods.
765 If the text value ends with C<at file line 1> as $@ strings do, then
766 this infomation will be used to set the C<-file> and C<-line> arguments
767 of the error object.
769 This class is used internally if an eval'd block die's with an error
770 that is a plain string. (Unless C<$Error::ObjectifyCallback> is modified)
772 =back
774 =head1 $Error::ObjectifyCallback
776 This variable holds a reference to a subroutine that converts errors that
777 are plain strings to objects. It is used by Error.pm to convert textual
778 errors to objects, and can be overrided by the user.
780 It accepts a single argument which is a hash reference to named parameters.
781 Currently the only named parameter passed is C<'text'> which is the text
782 of the error, but others may be available in the future.
784 For example the following code will cause Error.pm to throw objects of the
785 class MyError::Bar by default:
787     sub throw_MyError_Bar
788     {
789         my $args = shift;
790         my $err = MyError::Bar->new();
791         $err->{'MyBarText'} = $args->{'text'};
792         return $err;
793     }
795     {
796         local $Error::ObjectifyCallback = \&throw_MyError_Bar;
798         # Error handling here.
799     }
801 =head1 KNOWN BUGS
803 None, but that does not mean there are not any.
805 =head1 AUTHORS
807 Graham Barr <gbarr@pobox.com>
809 The code that inspired me to write this was originally written by
810 Peter Seibel <peter@weblogic.com> and adapted by Jesse Glick
811 <jglick@sig.bsh.com>.
813 =head1 MAINTAINER
815 Shlomi Fish <shlomif@iglu.org.il>
817 =head1 PAST MAINTAINERS
819 Arun Kumar U <u_arunkumar@yahoo.com>
821 =cut