X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCarp.pm;h=6199f8944877901a3db80d70f979fca99ff3ba70;hb=a6d0563455796929d2aae5a18fb57e80a20f87bd;hp=8301f37da7c107f88ea14b62380c04a186970c87;hpb=9efbc0eb9ef0a7b1489f59143775f10b17cd372d;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/Carp.pm b/lib/Carp.pm index 8301f37..6199f89 100644 --- a/lib/Carp.pm +++ b/lib/Carp.pm @@ -1,5 +1,7 @@ package Carp; +our $VERSION = '1.01'; + =head1 NAME carp - warn of errors (from perspective of caller) @@ -11,6 +13,10 @@ croak - die of errors (from perspective of caller) confess - die of errors with stack backtrace +shortmess - return the message that carp and croak produce + +longmess - return the message that cluck and confess produce + =head1 SYNOPSIS use Carp; @@ -19,14 +25,60 @@ confess - die of errors with stack backtrace use Carp qw(cluck); cluck "This is how we got here!"; + print FH Carp::shortmess("This will have caller's details added"); + print FH Carp::longmess("This will have stack backtrace added"); + =head1 DESCRIPTION The Carp routines are useful in your own modules because -they act like die() or warn(), but report where the error -was in the code they were called from. Thus if you have a -routine Foo() that has a carp() in it, then the carp() -will report the error as occurring where Foo() was called, -not where carp() was called. +they act like die() or warn(), but with a message which is more +likely to be useful to a user of your module. In the case of +cluck, confess, and longmess that context is a summary of every +call in the call-stack. For a shorter message you can use carp, +croak or shortmess which report the error as being from where +your module was called. There is no guarantee that that is where +the error was, but it is a good educated guess. + +Here is a more complete description of how shortmess works. What +it does is search the call-stack for a function call stack where +it hasn't been told that there shouldn't be an error. If every +call is marked safe, it then gives up and gives a full stack +backtrace instead. In other words it presumes that the first likely +looking potential suspect is guilty. Its rules for telling whether +a call shouldn't generate errors work as follows: + +=over 4 + +=item 1. + +Any call from a package to itself is safe. + +=item 2. + +Packages claim that there won't be errors on calls to or from +packages explicitly marked as safe by inclusion in @CARP_NOT, or +(if that array is empty) @ISA. The ability to override what +@ISA says is new in 5.8. + +=item 3. + +The trust in item 2 is transitive. If A trusts B, and B +trusts C, then A trusts C. So if you do not override @ISA +with @CARP_NOT, then this trust relationship is identical to, +"inherits from". + +=item 4. + +Any call from an internal Perl module is safe. (Nothing keeps +user modules from marking themselves as internal to Perl, but +this practice is discouraged.) + +=item 5. + +Any call to Carp is safe. (This rule is what keeps it from +reporting the error where you call carp/croak/shortmess.) + +=back =head2 Forcing a Stack Trace @@ -40,7 +92,7 @@ This feature is enabled by 'importing' the non-existent symbol perl -MCarp=verbose script.pl -or by including the string C in the L +or by including the string C in the PERL5OPT environment variable. =head1 BUGS @@ -57,12 +109,21 @@ call die() or warn(), as appropriate. # _almost_ complete understanding of the package. Corrections and # comments are welcome. -# The $CarpLevel variable can be set to "strip off" extra caller levels for -# those times when Carp calls are buried inside other functions. The +# The members of %Internal are packages that are internal to perl. +# Carp will not report errors from within these packages if it +# can. The members of %CarpInternal are internal to Perl's warning +# system. Carp will not report errors from within these packages +# either, and will not report calls *to* these packages for carp and +# croak. They replace $CarpLevel, which is deprecated. The # $Max(EvalLen|(Arg(Len|Nums)) variables are used to specify how the eval # text and function arguments should be formatted when printed. +$CarpInternal{Carp}++; +$CarpInternal{warnings}++; $CarpLevel = 0; # How many extra package levels to skip on carp. + # How many calls to skip on confess. + # Reconciling these notions is hard, use + # %Internal and %CarpInternal instead. $MaxEvalLen = 0; # How much eval '...text...' to show. 0 = all. $MaxArgLen = 64; # How much of each argument to print. 0 = all. $MaxArgNums = 8; # How many arguments to print. 0 = all. @@ -71,7 +132,7 @@ $Verbose = 0; # If true then make shortmess call longmess instead require Exporter; @ISA = ('Exporter'); @EXPORT = qw(confess croak carp); -@EXPORT_OK = qw(cluck verbose); +@EXPORT_OK = qw(cluck verbose longmess shortmess); @EXPORT_FAIL = qw(verbose); # hook to enable verbose mode @@ -94,101 +155,16 @@ sub export_fail { # each function call on the stack. sub longmess { - return @_ if ref $_[0]; - my $error = join '', @_; - my $mess = ""; - my $i = 1 + $CarpLevel; - my ($pack,$file,$line,$sub,$hargs,$eval,$require); - my (@a); - # - # crawl up the stack.... - # - while (do { { package DB; @a = caller($i++) } } ) { - # get copies of the variables returned from caller() - ($pack,$file,$line,$sub,$hargs,undef,$eval,$require) = @a; - # - # if the $error error string is newline terminated then it - # is copied into $mess. Otherwise, $mess gets set (at the end of - # the 'else {' section below) to one of two things. The first time - # through, it is set to the "$error at $file line $line" message. - # $error is then set to 'called' which triggers subsequent loop - # iterations to append $sub to $mess before appending the "$error - # at $file line $line" which now actually reads "called at $file line - # $line". Thus, the stack trace message is constructed: - # - # first time: $mess = $error at $file line $line - # subsequent times: $mess .= $sub $error at $file line $line - # ^^^^^^ - # "called" - if ($error =~ m/\n$/) { - $mess .= $error; - } else { - # Build a string, $sub, which names the sub-routine called. - # This may also be "require ...", "eval '...' or "eval {...}" - if (defined $eval) { - if ($require) { - $sub = "require $eval"; - } else { - $eval =~ s/([\\\'])/\\$1/g; - if ($MaxEvalLen && length($eval) > $MaxEvalLen) { - substr($eval,$MaxEvalLen) = '...'; - } - $sub = "eval '$eval'"; - } - } elsif ($sub eq '(eval)') { - $sub = 'eval {...}'; - } - # if there are any arguments in the sub-routine call, format - # them according to the format variables defined earlier in - # this file and join them onto the $sub sub-routine string - if ($hargs) { - # we may trash some of the args so we take a copy - @a = @DB::args; # must get local copy of args - # don't print any more than $MaxArgNums - if ($MaxArgNums and @a > $MaxArgNums) { - # cap the length of $#a and set the last element to '...' - $#a = $MaxArgNums; - $a[$#a] = "..."; - } - for (@a) { - # set args to the string "undef" if undefined - $_ = "undef", next unless defined $_; - if (ref $_) { - # dunno what this is for... - $_ .= ''; - s/'/\\'/g; - } - else { - s/'/\\'/g; - # terminate the string early with '...' if too long - substr($_,$MaxArgLen) = '...' - if $MaxArgLen and $MaxArgLen < length; - } - # 'quote' arg unless it looks like a number - $_ = "'$_'" unless /^-?[\d.]+$/; - # print high-end chars as 'M-' or '^' - s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg; - s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg; - } - # append ('all', 'the', 'arguments') to the $sub string - $sub .= '(' . join(', ', @a) . ')'; - } - # here's where the error message, $mess, gets constructed - $mess .= "\t$sub " if $error eq "called"; - $mess .= "$error at $file line $line"; - $mess .= " thread " . Thread->self->tid - if exists $main::{'Thread::'}; - $mess .= "\n"; - } - # we don't need to print the actual error message again so we can - # change this to "called" so that the string "$error at $file line - # $line" makes sense as "called at $file line $line". - $error = "called"; + { local $@; require Carp::Heavy; } # XXX fix require to not clear $@? + # Icky backwards compatibility wrapper. :-( + my $call_pack = caller(); + if ($Internal{$call_pack} or $CarpInternal{$call_pack}) { + return longmess_heavy(@_); + } + else { + local $CarpLevel = $CarpLevel + 1; + return longmess_heavy(@_); } - # this kludge circumvents die's incorrect handling of NUL - my $msg = \($mess || $error); - $$msg =~ tr/\0//d; - $$msg; } @@ -199,83 +175,11 @@ sub longmess { # you always get a stack trace sub shortmess { # Short-circuit &longmess if called via multiple packages - goto &longmess if $Verbose; - return @_ if ref $_[0]; - my $error = join '', @_; - my ($prevpack) = caller(1); - my $extra = $CarpLevel; - my $i = 2; - my ($pack,$file,$line); - # when reporting an error, we want to report it from the context of the - # calling package. So what is the calling package? Within a module, - # there may be many calls between methods and perhaps between sub-classes - # and super-classes, but the user isn't interested in what happens - # inside the package. We start by building a hash array which keeps - # track of all the packages to which the calling package belongs. We - # do this by examining its @ISA variable. Any call from a base class - # method (one of our caller's @ISA packages) can be ignored - my %isa = ($prevpack,1); - - # merge all the caller's @ISA packages into %isa. - @isa{@{"${prevpack}::ISA"}} = () - if(@{"${prevpack}::ISA"}); - - # now we crawl up the calling stack and look at all the packages in - # there. For each package, we look to see if it has an @ISA and then - # we see if our caller features in that list. That would imply that - # our caller is a derived class of that package and its calls can also - # be ignored - while (($pack,$file,$line) = caller($i++)) { - if(@{$pack . "::ISA"}) { - my @i = @{$pack . "::ISA"}; - my %i; - @i{@i} = (); - # merge any relevant packages into %isa - @isa{@i,$pack} = () - if(exists $i{$prevpack} || exists $isa{$pack}); - } - - # and here's where we do the ignoring... if the package in - # question is one of our caller's base or derived packages then - # we can ignore it (skip it) and go onto the next (but note that - # the continue { } block below gets called every time) - next - if(exists $isa{$pack}); - - # Hey! We've found a package that isn't one of our caller's - # clan....but wait, $extra refers to the number of 'extra' levels - # we should skip up. If $extra > 0 then this is a false alarm. - # We must merge the package into the %isa hash (so we can ignore it - # if it pops up again), decrement $extra, and continue. - if ($extra-- > 0) { - %isa = ($pack,1); - @isa{@{$pack . "::ISA"}} = () - if(@{$pack . "::ISA"}); - } - else { - # OK! We've got a candidate package. Time to construct the - # relevant error message and return it. die() doesn't like - # to be given NUL characters (which $msg may contain) so we - # remove them first. - my $msg; - $msg = "$error at $file line $line"; - $msg .= " thread " . Thread->self->tid - if exists $main::{'Thread::'}; - $msg .= "\n"; - $msg =~ tr/\0//d; - return $msg; - } - } - continue { - $prevpack = $pack; - } - - # uh-oh! It looks like we crawled all the way up the stack and - # never found a candidate package. Oh well, let's call longmess - # to generate a full stack trace. We use the magical form of 'goto' - # so that this shortmess() function doesn't appear on the stack - # to further confuse longmess() about it's calling package. - goto &longmess; + { local $@; require Carp::Heavy; } # XXX fix require to not clear $@? + # Icky backwards compatibility wrapper. :-( + my $call_pack = caller(); + local @CARP_NOT = caller(); + shortmess_heavy(@_); }