Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/Sub/Uplevel.pm view on Meta::CPAN
#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
( run in 1.941 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )