File Coverage

inc/Sub/Uplevel.pm
Criterion Covered Total %
statement 25 69 36.2
branch 4 28 14.2
condition 2 11 18.1
subroutine 6 10 60.0
pod 1 1 100.0
total 38 119 31.9


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