X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FTry%2FTiny.pm;h=cf8f1f727898b2f39248419adfc4a6ba49e01301;hb=8447a3bf0e1b88bdd07fa584d83c81c64ad192cf;hp=91307b465bd7105d4fae21814641b9df89224293;hpb=79039ae433128d824ddf3d930909434f9fe645d3;p=p5sagit%2FTry-Tiny.git diff --git a/lib/Try/Tiny.pm b/lib/Try/Tiny.pm index 91307b4..cf8f1f7 100644 --- a/lib/Try/Tiny.pm +++ b/lib/Try/Tiny.pm @@ -2,6 +2,8 @@ package Try::Tiny; use 5.006; # ABSTRACT: minimal try/catch with proper preservation of $@ +our $VERSION = '0.25'; + use strict; use warnings; @@ -11,7 +13,23 @@ our @EXPORT = our @EXPORT_OK = qw(try catch finally); use Carp; $Carp::Internal{+__PACKAGE__}++; -BEGIN { eval "use Sub::Name; 1" or *{subname} = sub {1} } +BEGIN { + my $su = $INC{'Sub/Util.pm'} && defined &Sub::Util::set_subname; + my $sn = $INC{'Sub/Name.pm'} && eval { Sub::Name->VERSION(0.08) }; + unless ($su || $sn) { + $su = eval { require Sub::Util; } && defined &Sub::Util::set_subname; + unless ($su) { + $sn = eval { require Sub::Name; Sub::Name->VERSION(0.08) }; + } + } + + *_subname = $su ? \&Sub::Util::set_subname + : $sn ? \&Sub::Name::subname + : sub { $_[1] }; + *_HAS_SUBNAME = ($su || $sn) ? sub(){1} : sub(){0}; +} + +my @_finally_guards; # Need to prototype as @ not $$ because of the way Perl evaluates the prototype. # Keeping it at $$ means you only ever get 1 sub because we need to eval in a list @@ -53,9 +71,17 @@ sub try (&;@) { # name the blocks if we have Sub::Name installed my $caller = caller; - subname("${caller}::try {...} " => $try); - subname("${caller}::catch {...} " => $catch) if $catch; - subname("${caller}::finally {...} " => $_) foreach @finally; + _subname("${caller}::try {...} " => $try) + if _HAS_SUBNAME; + + # set up scope guards to invoke the finally blocks at the end. + # this should really be a function scope lexical variable instead of + # file scope + local but that causes issues with perls < 5.20 due to + # perl rt#119311 + local $_finally_guards[0] = [ + map { Try::Tiny::ScopeGuard->_new($_) } + @finally + ]; # save the value of $@ so we can set $@ back to it in the beginning of the eval # and restore $@ after the eval finishes @@ -77,21 +103,19 @@ sub try (&;@) { $try->(); }; - return 1; # properly set $fail to false + return 1; # properly set $failed to false }; # preserve the current error and reset the original value of $@ $error = $@; $@ = $prev_error; - # set up a scope guard to invoke the finally block at the end - my @guards = - map { Try::Tiny::ScopeGuard->_new($_, $failed ? $error : ()) } - @finally; - # at this point $failed contains a true value if the eval died, even if some # destructor overwrote $@ as the eval was unwinding. if ( $failed ) { + # pass $error to the finally blocks + push @$_, $error for @{$_finally_guards[0]}; + # if we got an error, invoke the catch block. if ( $catch ) { # This works like given($error), but is backwards compatible and @@ -116,6 +140,9 @@ sub catch (&;@) { croak 'Useless bare catch()' unless wantarray; + my $caller = caller; + _subname("${caller}::catch {...} " => $block) + if _HAS_SUBNAME; return ( bless(\$block, 'Try::Tiny::Catch'), @rest, @@ -127,6 +154,9 @@ sub finally (&;@) { croak 'Useless bare finally()' unless wantarray; + my $caller = caller; + _subname("${caller}::finally {...} " => $block) + if _HAS_SUBNAME; return ( bless(\$block, 'Try::Tiny::Finally'), @rest, @@ -217,8 +247,8 @@ context or the empty list in list context. The following examples all assign C<"bar"> to C<$x>: my $x = try { die "foo" } catch { "bar" }; - my $x = try { die "foo" } || { "bar" }; - my $x = (try { die "foo" }) // { "bar" }; + my $x = try { die "foo" } || "bar"; + my $x = (try { die "foo" }) // "bar"; my $x = eval { die "foo" } || "bar"; @@ -443,6 +473,8 @@ value. Using Perl 5.10 you can use L. +=for stopwords topicalizer + The C block is invoked in a topicalizer context (like a C block), but note that you can't return a useful value from C using the C blocks without an explicit C. @@ -464,7 +496,7 @@ concisely match errors: =item * C<@_> is not available within the C block, so you need to copy your -arglist. In case you want to work with argument values directly via C<@_> +argument list. In case you want to work with argument values directly via C<@_> aliasing (i.e. allow C<$_[1] = "foo">), you need to pass C<@_> by reference: sub foo { @@ -532,11 +564,13 @@ C introduces another caller stack frame. L is not used. L is used. This lack of magic is considered a feature. +=for stopwords unhygienically + =item * The value of C<$_> in the C block is not guaranteed to be the value of the exception thrown (C<$@>) in the C block. There is no safe way to -ensure this, since C may be used unhygenically in destructors. The only +ensure this, since C may be used unhygienically in destructors. The only guarantee is that the C will be called if an exception is thrown. =item * @@ -584,7 +618,7 @@ confusing behavior: Note that this behavior was changed once again in L. -However, since the entirety of lexical C<$_> is now L is now L, it is unclear whether the new version 18 behavior is final.