Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/Sub/Uplevel.pm view on Meta::CPAN
package Sub::Uplevel;
use 5.006;
use strict;
# ABSTRACT: apparently run a function in a higher stack frame
our $VERSION = '0.2600';
# Frame check global constant
our $CHECK_FRAMES;
BEGIN {
$CHECK_FRAMES = !! $CHECK_FRAMES;
}
use constant CHECK_FRAMES => $CHECK_FRAMES;
# We must override *CORE::GLOBAL::caller if it hasn't already been
# overridden or else Perl won't see our local override later.
if ( not defined *CORE::GLOBAL::caller{CODE} ) {
*CORE::GLOBAL::caller = \&_normal_caller;
}
# modules to force reload if ":aggressive" is specified
my @reload_list = qw/Exporter Exporter::Heavy/;
sub import {
no strict 'refs'; ## no critic
my ($class, @args) = @_;
for my $tag ( @args, 'uplevel' ) {
if ( $tag eq 'uplevel' ) {
my $caller = caller(0);
*{"$caller\::uplevel"} = \&uplevel;
}
elsif( $tag eq ':aggressive' ) {
_force_reload( @reload_list );
}
else {
die qq{"$tag" is not exported by the $class module\n}
}
}
return;
}
sub _force_reload {
no warnings 'redefine';
local $^W = 0;
for my $m ( @_ ) {
$m =~ s{::}{/}g;
$m .= ".pm";
require $m if delete $INC{$m};
}
}
#pod =head1 SYNOPSIS
#pod
#pod use Sub::Uplevel;
#pod
#pod sub foo {
#pod print join " - ", caller;
#pod }
#pod
#pod sub bar {
#pod uplevel 1, \&foo;
#pod }
#pod
local/lib/perl5/Sub/Uplevel.pm view on Meta::CPAN
#pod
#pod Like Tcl's uplevel() function, but not quite so dangerous. The idea
#pod is just to fool caller(). All the really naughty bits of Tcl's
#pod uplevel() are avoided.
#pod
#pod B<THIS IS NOT THE SORT OF THING YOU WANT TO DO EVERYDAY>
#pod
#pod =over 4
#pod
#pod =item B<uplevel>
#pod
#pod uplevel $num_frames, \&func, @args;
#pod
#pod Makes the given function think it's being executed $num_frames higher
#pod than the current stack level. So when they use caller($frames) it
#pod will actually give caller($frames + $num_frames) for them.
#pod
#pod C<uplevel(1, \&some_func, @_)> is effectively C<goto &some_func> but
#pod you don't immediately exit the current subroutine. So while you can't
#pod do this:
#pod
#pod sub wrapper {
#pod print "Before\n";
#pod goto &some_func;
#pod print "After\n";
#pod }
#pod
#pod you can do this:
#pod
#pod sub wrapper {
#pod print "Before\n";
#pod my @out = uplevel 1, &some_func;
#pod print "After\n";
#pod return @out;
#pod }
#pod
#pod C<uplevel> has the ability to issue a warning if C<$num_frames> is more than
#pod the current call stack depth, although this warning is disabled and compiled
#pod out by default as the check is relatively expensive.
#pod
#pod To enable the check for debugging or testing, you should set the global
#pod C<$Sub::Uplevel::CHECK_FRAMES> to true before loading Sub::Uplevel for the
#pod first time as follows:
#pod
#pod #!/usr/bin/perl
#pod
#pod BEGIN {
#pod $Sub::Uplevel::CHECK_FRAMES = 1;
#pod }
#pod use Sub::Uplevel;
#pod
#pod Setting or changing the global after the module has been loaded will have
#pod no effect.
#pod
#pod =cut
# @Up_Frames -- uplevel stack
# $Caller_Proxy -- whatever caller() override was in effect before uplevel
our (@Up_Frames, $Caller_Proxy);
sub _apparent_stack_height {
my $height = 1; # start above this function
while ( 1 ) {
last if ! defined scalar $Caller_Proxy->($height);
$height++;
}
return $height - 1; # subtract 1 for this function
}
sub uplevel {
# Backwards compatible version of "no warnings 'redefine'"
my $old_W = $^W;
$^W = 0;
# Update the caller proxy if the uplevel override isn't in effect
local $Caller_Proxy = *CORE::GLOBAL::caller{CODE}
if *CORE::GLOBAL::caller{CODE} != \&_uplevel_caller;
local *CORE::GLOBAL::caller = \&_uplevel_caller;
# Restore old warnings state
$^W = $old_W;
if ( CHECK_FRAMES and $_[0] >= _apparent_stack_height() ) {
require Carp;
Carp::carp("uplevel $_[0] is more than the caller stack");
}
local @Up_Frames = (shift, @Up_Frames );
my $function = shift;
return $function->(@_);
}
sub _normal_caller (;$) { ## no critic Prototypes
my ($height) = @_;
$height++;
my @caller = CORE::caller($height);
if ( CORE::caller() eq 'DB' ) {
# Oops, redo picking up @DB::args
package DB;
@caller = CORE::caller($height);
}
return if ! @caller; # empty
return $caller[0] if ! wantarray; # scalar context
return @_ ? @caller : @caller[0..2]; # extra info or regular
}
sub _uplevel_caller (;$) { ## no critic Prototypes
my $height = $_[0] || 0;
# shortcut if no uplevels have been called
# always add +1 to CORE::caller (proxy caller function)
# to skip this function's caller
return $Caller_Proxy->( $height + 1 ) if ! @Up_Frames;
#pod =begin _private
#pod
#pod So it has to work like this:
#pod
#pod Call stack Actual uplevel 1
#pod CORE::GLOBAL::caller
#pod Carp::short_error_loc 0
#pod Carp::shortmess_heavy 1 0
#pod Carp::croak 2 1
#pod try_croak 3 2
#pod uplevel 4
#pod function_that_called_uplevel 5
#pod caller_we_want_to_see 6 3
#pod its_caller 7 4
#pod
#pod So when caller(X) winds up below uplevel(), it only has to use
#pod CORE::caller(X+1) (to skip CORE::GLOBAL::caller). But when caller(X)
#pod winds up no or above uplevel(), it's CORE::caller(X+1+uplevel+1).
#pod
#pod Which means I'm probably going to have to do something nasty like walk
#pod up the call stack on each caller() to see if I'm going to wind up
#pod before or after Sub::Uplevel::uplevel().
#pod
#pod =end _private
#pod
#pod =begin _dagolden
#pod
local/lib/perl5/Sub/Uplevel.pm view on Meta::CPAN
#pod
#pod =head1 BUGS and CAVEATS
#pod
#pod Well, the bad news is uplevel() is about 5 times slower than a normal
#pod function call. XS implementation anyone? It also slows down every invocation
#pod of caller(), regardless of whether uplevel() is in effect.
#pod
#pod Sub::Uplevel overrides CORE::GLOBAL::caller temporarily for the scope of
#pod each uplevel call. It does its best to work with any previously existing
#pod CORE::GLOBAL::caller (both when Sub::Uplevel is first loaded and within
#pod each uplevel call) such as from Contextual::Return or Hook::LexWrap.
#pod
#pod However, if you are routinely using multiple modules that override
#pod CORE::GLOBAL::caller, you are probably asking for trouble.
#pod
#pod You B<should> load Sub::Uplevel as early as possible within your program. As
#pod with all CORE::GLOBAL overloading, the overload will not affect modules that
#pod have already been compiled prior to the overload. One module that often is
#pod unavoidably loaded prior to Sub::Uplevel is Exporter. To forcibly recompile
#pod Exporter (and Exporter::Heavy) after loading Sub::Uplevel, use it with the
#pod ":aggressive" tag:
#pod
#pod use Sub::Uplevel qw/:aggressive/;
#pod
#pod The private function C<Sub::Uplevel::_force_reload()> may be passed a list of
#pod additional modules to reload if ":aggressive" is not aggressive enough.
#pod Reloading modules may break things, so only use this as a last resort.
#pod
#pod As of version 0.20, Sub::Uplevel requires Perl 5.6 or greater.
#pod
#pod =head1 HISTORY
#pod
#pod Those who do not learn from HISTORY are doomed to repeat it.
#pod
#pod The lesson here is simple: Don't sit next to a Tcl programmer at the
#pod dinner table.
#pod
#pod =head1 THANKS
#pod
#pod Thanks to Brent Welch, Damian Conway and Robin Houston.
#pod
#pod See http://www.perl.com/perl/misc/Artistic.html
#pod
#pod =head1 SEE ALSO
#pod
#pod PadWalker (for the similar idea with lexicals), Hook::LexWrap,
#pod Tcl's uplevel() at http://www.scriptics.com/man/tcl8.4/TclCmd/uplevel.htm
#pod
#pod =cut
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Sub::Uplevel - apparently run a function in a higher stack frame
=head1 VERSION
version 0.2600
=head1 SYNOPSIS
use Sub::Uplevel;
sub foo {
print join " - ", caller;
}
sub bar {
uplevel 1, \&foo;
}
#line 11
bar(); # main - foo.plx - 11
=head1 DESCRIPTION
Like Tcl's uplevel() function, but not quite so dangerous. The idea
is just to fool caller(). All the really naughty bits of Tcl's
uplevel() are avoided.
B<THIS IS NOT THE SORT OF THING YOU WANT TO DO EVERYDAY>
=over 4
=item B<uplevel>
uplevel $num_frames, \&func, @args;
Makes the given function think it's being executed $num_frames higher
than the current stack level. So when they use caller($frames) it
will actually give caller($frames + $num_frames) for them.
C<uplevel(1, \&some_func, @_)> is effectively C<goto &some_func> but
you don't immediately exit the current subroutine. So while you can't
do this:
sub wrapper {
print "Before\n";
goto &some_func;
print "After\n";
}
you can do this:
sub wrapper {
print "Before\n";
my @out = uplevel 1, &some_func;
print "After\n";
return @out;
}
C<uplevel> has the ability to issue a warning if C<$num_frames> is more than
the current call stack depth, although this warning is disabled and compiled
out by default as the check is relatively expensive.
( run in 0.748 second using v1.01-cache-2.11-cpan-5735350b133 )