📄 error.pm
字号:
# Error.pm
#
# Copyright (c) 1997-8 Graham Barr <gbarr@ti.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# Based on my original Error.pm, and Exceptions.pm by Peter Seibel
# <peter@weblogic.com> and adapted by Jesse Glick <jglick@sig.bsh.com>.
#
# but modified ***significantly***
package Error;
use strict;
use vars qw($VERSION);
use 5.004;
$VERSION = "0.15";
use overload (
'""' => 'stringify',
'0+' => 'value',
'bool' => sub { return 1; },
'fallback' => 1
);
$Error::Depth = 0; # Depth to pass to caller()
$Error::Debug = 0; # Generate verbose stack traces
@Error::STACK = (); # Clause stack for try
$Error::THROWN = undef; # last error thrown, a workaround until die $ref works
my $LAST; # Last error created
my %ERROR; # Last error associated with package
# Exported subs are defined in Error::subs
sub import {
shift;
local $Exporter::ExportLevel = $Exporter::ExportLevel + 1;
Error::subs->import(@_);
}
# I really want to use last for the name of this method, but it is a keyword
# which prevent the syntax last Error
sub prior {
shift; # ignore
return $LAST unless @_;
my $pkg = shift;
return exists $ERROR{$pkg} ? $ERROR{$pkg} : undef
unless ref($pkg);
my $obj = $pkg;
my $err = undef;
if($obj->isa('HASH')) {
$err = $obj->{'__Error__'}
if exists $obj->{'__Error__'};
}
elsif($obj->isa('GLOB')) {
$err = ${*$obj}{'__Error__'}
if exists ${*$obj}{'__Error__'};
}
$err;
}
# Return as much information as possible about where the error
# happened. The -stacktrace element only exists if $Error::DEBUG
# was set when the error was created
sub stacktrace {
my $self = shift;
return $self->{'-stacktrace'}
if exists $self->{'-stacktrace'};
my $text = exists $self->{'-text'} ? $self->{'-text'} : "Died";
$text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
unless($text =~ /\n$/s);
$text;
}
# Allow error propagation, ie
#
# $ber->encode(...) or
# return Error->prior($ber)->associate($ldap);
sub associate {
my $err = shift;
my $obj = shift;
return unless ref($obj);
if($obj->isa('HASH')) {
$obj->{'__Error__'} = $err;
}
elsif($obj->isa('GLOB')) {
${*$obj}{'__Error__'} = $err;
}
$obj = ref($obj);
$ERROR{ ref($obj) } = $err;
return;
}
sub new {
my $self = shift;
my($pkg,$file,$line) = caller($Error::Depth);
my $err = bless {
'-package' => $pkg,
'-file' => $file,
'-line' => $line,
@_
}, $self;
$err->associate($err->{'-object'})
if(exists $err->{'-object'});
# To always create a stacktrace would be very inefficient, so
# we only do it if $Error::Debug is set
if($Error::Debug) {
require Carp;
local $Carp::CarpLevel = $Error::Depth;
my $text = defined($err->{'-text'}) ? $err->{'-text'} : "Error";
my $trace = Carp::longmess($text);
# Remove try calls from the trace
$trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
$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;
$err->{'-stacktrace'} = $trace
}
$@ = $LAST = $ERROR{$pkg} = $err;
}
# Throw an error. this contains some very gory code.
sub throw {
my $self = shift;
local $Error::Depth = $Error::Depth + 1;
# if we are not rethrow-ing then create the object to throw
$self = $self->new(@_) unless ref($self);
die $Error::THROWN = $self;
}
# syntactic sugar for
#
# die with Error( ... );
sub with {
my $self = shift;
local $Error::Depth = $Error::Depth + 1;
$self->new(@_);
}
# syntactic sugar for
#
# record Error( ... ) and return;
sub record {
my $self = shift;
local $Error::Depth = $Error::Depth + 1;
$self->new(@_);
}
# catch clause for
#
# try { ... } catch CLASS with { ... }
sub catch {
my $pkg = shift;
my $code = shift;
my $clauses = shift || {};
my $catch = $clauses->{'catch'} ||= [];
unshift @$catch, $pkg, $code;
$clauses;
}
# Object query methods
sub object {
my $self = shift;
exists $self->{'-object'} ? $self->{'-object'} : undef;
}
sub file {
my $self = shift;
exists $self->{'-file'} ? $self->{'-file'} : undef;
}
sub line {
my $self = shift;
exists $self->{'-line'} ? $self->{'-line'} : undef;
}
sub text {
my $self = shift;
exists $self->{'-text'} ? $self->{'-text'} : undef;
}
# overload methods
sub stringify {
my $self = shift;
defined $self->{'-text'} ? $self->{'-text'} : "Died";
}
sub value {
my $self = shift;
exists $self->{'-value'} ? $self->{'-value'} : undef;
}
package Error::Simple;
@Error::Simple::ISA = qw(Error);
sub new {
my $self = shift;
my $text = "" . shift;
my $value = shift;
my(@args) = ();
local $Error::Depth = $Error::Depth + 1;
@args = ( -file => $1, -line => $2)
if($text =~ s/ at (\S+) line (\d+)(\.\n)?$//s);
push(@args, '-value', 0 + $value)
if defined($value);
$self->SUPER::new(-text => $text, @args);
}
sub stringify {
my $self = shift;
my $text = $self->SUPER::stringify;
$text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
unless($text =~ /\n$/s);
$text;
}
##########################################################################
##########################################################################
# Inspired by code from Jesse Glick <jglick@sig.bsh.com> and
# Peter Seibel <peter@weblogic.com>
package Error::subs;
use Exporter ();
use vars qw(@EXPORT_OK @ISA %EXPORT_TAGS);
@EXPORT_OK = qw(try with finally except otherwise);
%EXPORT_TAGS = (try => \@EXPORT_OK);
@ISA = qw(Exporter);
sub run_clauses ($$$\@) {
my($clauses,$err,$wantarray,$result) = @_;
my $code = undef;
$err = new Error::Simple($err) unless ref($err);
CATCH: {
# catch
my $catch;
if(defined($catch = $clauses->{'catch'})) {
my $i = 0;
CATCHLOOP:
for( ; $i < @$catch ; $i += 2) {
my $pkg = $catch->[$i];
unless(defined $pkg) {
#except
splice(@$catch,$i,2,$catch->[$i+1]->());
$i -= 2;
next CATCHLOOP;
}
elsif($err->isa($pkg)) {
$code = $catch->[$i+1];
while(1) {
my $more = 0;
local($Error::THROWN);
my $ok = eval {
if($wantarray) {
@{$result} = $code->($err,\$more);
}
elsif(defined($wantarray)) {
@{$result} = ();
$result->[0] = $code->($err,\$more);
}
else {
$code->($err,\$more);
}
1;
};
if( $ok ) {
next CATCHLOOP if $more;
undef $err;
}
else {
$err = defined($Error::THROWN)
? $Error::THROWN : $@;
$err = new Error::Simple($err)
unless ref($err);
}
last CATCH;
};
}
}
}
# otherwise
my $owise;
if(defined($owise = $clauses->{'otherwise'})) {
my $code = $clauses->{'otherwise'};
my $more = 0;
my $ok = eval {
if($wantarray) {
@{$result} = $code->($err,\$more);
}
elsif(defined($wantarray)) {
@{$result} = ();
$result->[0] = $code->($err,\$more);
}
else {
$code->($err,\$more);
}
1;
};
if( $ok ) {
undef $err;
}
else {
$err = defined($Error::THROWN)
? $Error::THROWN : $@;
$err = new Error::Simple($err)
unless ref($err);
}
}
}
$err;
}
sub try (&;$) {
my $try = shift;
my $clauses = @_ ? shift : {};
my $ok = 0;
my $err = undef;
my @result = ();
unshift @Error::STACK, $clauses;
my $wantarray = wantarray();
do {
local $Error::THROWN = undef;
$ok = eval {
if($wantarray) {
@result = $try->();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -