File Coverage

inc/Sub/Uplevel.pm
Criterion Covered Total %
statement 52 67 77.6
branch 17 30 56.6
condition 3 5 60.0
subroutine 10 12 83.3
pod 1 1 100.0
total 83 115 72.1


line stmt bran cond sub pod time code
1             #line 1
2 1     1   24 package Sub::Uplevel;
  1         4  
  1         49  
3 1     1   6 use 5.006;
  1         10  
  1         74  
4             use strict;
5             # ABSTRACT: apparently run a function in a higher stack frame
6             our $VERSION = '0.24'; # VERSION
7              
8             # Frame check global constant
9             our $CHECK_FRAMES;
10 1     1   38 BEGIN {
11             $CHECK_FRAMES = !! $CHECK_FRAMES;
12 1     1   6 }
  1         2  
  1         164  
13             use constant CHECK_FRAMES => $CHECK_FRAMES;
14              
15             # We must override *CORE::GLOBAL::caller if it hasn't already been
16             # overridden or else Perl won't see our local override later.
17              
18             if ( not defined *CORE::GLOBAL::caller{CODE} ) {
19             *CORE::GLOBAL::caller = \&_normal_caller;
20             }
21              
22             # modules to force reload if ":aggressive" is specified
23             my @reload_list = qw/Exporter Exporter::Heavy/;
24              
25 1     1   7 sub import {
  1         1  
  1         296  
26 1     1   4 no strict 'refs'; ## no critic
27 1         3 my ($class, @args) = @_;
28 2 50       7 for my $tag ( @args, 'uplevel' ) {
    0          
29 2         5 if ( $tag eq 'uplevel' ) {
30 2         3 my $caller = caller(0);
  2         13  
31             *{"$caller\::uplevel"} = \&uplevel;
32             }
33 0         0 elsif( $tag eq ':aggressive' ) {
34             _force_reload( @reload_list );
35             }
36 0         0 else {
37             die qq{"$tag" is not exported by the $class module\n}
38             }
39 1         27 }
40             return;
41             }
42              
43 1     1   7 sub _force_reload {
  1         2  
  1         12121  
44 0     0   0 no warnings 'redefine';
45 0         0 local $^W = 0;
46 0         0 for my $m ( @_ ) {
47 0         0 $m =~ s{::}{/}g;
48 0 0       0 $m .= ".pm";
49             require $m if delete $INC{$m};
50             }
51             }
52              
53              
54             # @Up_Frames -- uplevel stack
55             # $Caller_Proxy -- whatever caller() override was in effect before uplevel
56             our (@Up_Frames, $Caller_Proxy);
57              
58 0     0   0 sub _apparent_stack_height {
59 0         0 my $height = 1; # start above this function
60 0 0       0 while ( 1 ) {
61 0         0 last if ! defined scalar $Caller_Proxy->($height);
62             $height++;
63 0         0 }
64             return $height - 1; # subtract 1 for this function
65             }
66              
67             sub uplevel {
68 1     1 1 3 # Backwards compatible version of "no warnings 'redefine'"
69 1         3 my $old_W = $^W;
70             $^W = 0;
71              
72 1 50       6 # Update the caller proxy if the uplevel override isn't in effect
73             local $Caller_Proxy = *CORE::GLOBAL::caller{CODE}
74 1         4 if *CORE::GLOBAL::caller{CODE} != \&_uplevel_caller;
75             local *CORE::GLOBAL::caller = \&_uplevel_caller;
76              
77 1         3 # Restore old warnings state
78             $^W = $old_W;
79 1         2  
80             if ( CHECK_FRAMES and $_[0] >= _apparent_stack_height() ) {
81             require Carp;
82             Carp::carp("uplevel $_[0] is more than the caller stack");
83             }
84 1         3  
85             local @Up_Frames = (shift, @Up_Frames );
86 1         2  
87 1         4 my $function = shift;
88             return $function->(@_);
89             }
90              
91 7     7   83169 sub _normal_caller (;$) { ## no critic Prototypes
92 7         13 my ($height) = @_;
93 7         67 $height++;
94 7 50       36 my @caller = CORE::caller($height);
95             if ( CORE::caller() eq 'DB' ) {
96             # Oops, redo picking up @DB::args
97 0         0 package DB;
98             @caller = CORE::caller($height);
99             }
100 7 50       24  
101 7 100       31 return if ! @caller; # empty
102 4 100       35 return $caller[0] if ! wantarray; # scalar context
103             return @_ ? @caller : @caller[0..2]; # extra info or regular
104             }
105              
106 8   100 8   22 sub _uplevel_caller (;$) { ## no critic Prototypes
107             my $height = $_[0] || 0;
108              
109             # shortcut if no uplevels have been called
110             # always add +1 to CORE::caller (proxy caller function)
111 8 50       16 # to skip this function's caller
112             return $Caller_Proxy->( $height + 1 ) if ! @Up_Frames;
113              
114 8         8  
115 8         8 my $saw_uplevel = 0;
116             my $adjust = 0;
117              
118             # walk up the call stack to fight the right package level to return;
119             # look one higher than requested for each call to uplevel found
120             # and adjust by the amount found in the Up_Frames stack for that call.
121             # We *must* use CORE::caller here since we need the real stack not what
122             # some other override says the stack looks like, just in case that other
123             # override breaks things in some horrible way
124 8         20  
125 28         165 for ( my $up = 0; $up <= $height + $adjust; $up++ ) {
126 28 50 33     171 my @caller = CORE::caller($up + 1);
127             if( defined $caller[0] && $caller[0] eq __PACKAGE__ ) {
128             # add one for each uplevel call seen
129 0         0 # and look into the uplevel stack for the offset
130 0         0 $adjust += 1 + $Up_Frames[$saw_uplevel];
131             $saw_uplevel++;
132             }
133             }
134              
135             # For returning values, we pass through the call to the proxy caller
136 8         21 # function, just at a higher stack level
137 8 100       22 my @caller = $Caller_Proxy->($height + $adjust + 1);
138             if ( CORE::caller() eq 'DB' ) {
139             # Oops, redo picking up @DB::args
140 1         4 package DB;
141             @caller = $Sub::Uplevel::Caller_Proxy->($height + $adjust + 1);
142             }
143 8 50       21  
144 8 100       131 return if ! @caller; # empty
145 2 100       69 return $caller[0] if ! wantarray; # scalar context
146             return @_ ? @caller : @caller[0..2]; # extra info or regular
147             }
148              
149              
150             1;
151              
152             __END__