File Coverage

blib/lib/Sub/Defer.pm
Criterion Covered Total %
statement 110 110 100.0
branch 52 54 100.0
condition 49 63 77.7
subroutine 23 23 100.0
pod 5 5 100.0
total 239 255 94.5


line stmt bran cond sub pod time code
1             package Sub::Defer;
2 13     13   1276995 use strict;
  13         27  
  13         515  
3 13     13   90 use warnings;
  13         24  
  13         1063  
4              
5             our $VERSION = '2.006009';
6             $VERSION =~ tr/_//d;
7              
8 13     13   93 use Exporter ();
  13         24  
  13         443  
9 13     13   457 BEGIN { *import = \&Exporter::import }
10 13     13   136 use Scalar::Util qw(weaken);
  13         29  
  13         1069  
11 13     13   75 use Carp qw(croak);
  13         73  
  13         1431  
12              
13             our @EXPORT = qw(defer_sub undefer_sub undefer_all);
14             our @EXPORT_OK = qw(undefer_package defer_info);
15              
16 13     13   74 sub _getglob { no strict 'refs'; \*{$_[0]} }
  13     84   37  
  13         2953  
  84         112  
  84         494  
17              
18             BEGIN {
19 13     13   52 my $no_subname;
20             *_subname
21             = defined &Sub::Util::set_subname ? \&Sub::Util::set_subname
22             : defined &Sub::Name::subname ? \&Sub::Name::subname
23             : (eval { require Sub::Util } && defined &Sub::Util::set_subname) ? \&Sub::Util::set_subname
24             : (eval { require Sub::Name } && defined &Sub::Name::subname ) ? \&Sub::Name::subname
25 13 100 66 31   75 : ($no_subname = 1, sub { $_[1] });
  1 100 66     225984  
    100          
    100          
26 13 100       2141 *_CAN_SUBNAME = $no_subname ? sub(){0} : sub(){1};
27             }
28              
29             sub _name_coderef {
30 26 100   26   64 shift if @_ > 2; # three args is (target, name, sub)
31 26         222 _CAN_SUBNAME ? _subname(@_) : $_[1];
32             }
33              
34             sub _install_coderef {
35 26     26   1913 my ($glob, $code) = (_getglob($_[0]), _name_coderef(@_));
36 13     13   83 no warnings 'redefine';
  13         28  
  13         1079  
37 26 100       37 if (*{$glob}{CODE}) {
  26         95  
38 3         6 *{$glob} = $code;
  3         13  
39             }
40             # perl will sometimes warn about mismatched prototypes coming from the
41             # inheritance cache, so disable them if we aren't redefining a sub
42             else {
43 13     13   93 no warnings 'prototype';
  13         22  
  13         3215  
44 23         32 *{$glob} = $code;
  23         70  
45             }
46             }
47              
48             # We are dealing with three subs. The first is the generator sub. It is
49             # provided by the user, so we cannot modify it. When called, it generates the
50             # undeferred sub. This is also created, so it also cannot be modified. These
51             # are wrapped in a third sub. The deferred sub is generated by us, and when
52             # called it uses the generator sub to create the undeferred sub. If it is a
53             # named sub, it is installed in the symbol table, usually overwriting the
54             # deferred sub. From then on, the deferred sub will goto the undeferred sub
55             # if it is called.
56             #
57             # In %DEFERRED we store array refs with information about these subs. The key
58             # is the stringified subref. We have a CLONE method to fix this up in the
59             # case of threading to deal with changing refaddrs. The arrayrefs contain:
60             #
61             # 0. fully qualified sub name (or undef)
62             # 1. generator sub
63             # 2. options (attributes)
64             # 3. scalar ref to undeferred sub (inner reference weakened)
65             # 4. deferred sub (deferred only)
66             # 5. info arrayref for undeferred sub (deferred only, after undefer)
67             #
68             # The deferred sub contains a strong reference to its info arrayref, and the
69             # undeferred.
70              
71             our %DEFERRED;
72              
73             sub undefer_sub {
74 98     98 1 8225 my ($deferred) = @_;
75 98 100       358 my $info = $DEFERRED{$deferred} or return $deferred;
76 86         261 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
77              
78 86 100 66     371 if (!(
      66        
      100        
79             $deferred_sub && $deferred eq $deferred_sub
80 14         42 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  12         53  
81             )) {
82 2         8 return $deferred;
83             }
84              
85 22         83 return ${$undeferred_ref}
86 84 100       144 if ${$undeferred_ref};
  84         182  
87 62         149 ${$undeferred_ref} = my $made = $maker->();
  61         357  
88              
89             # make sure the method slot has not changed since deferral time
90 61 100 100     273 if (defined($target) && $deferred eq *{_getglob($target)}{CODE}||'') {
  30   100     85  
91 13     13   135 no warnings 'redefine';
  13         23  
  13         12232  
92              
93             # I believe $maker already evals with the right package/name, so that
94             # _install_coderef calls are not necessary --ribasushi
95 28         44 *{_getglob($target)} = $made;
  28         87  
96             }
97 61         155 my $undefer_info = [ $target, $maker, $options, $undeferred_ref ];
98 61         264 $info->[5] = $DEFERRED{$made} = $undefer_info;
99 61         89 weaken ${$undefer_info->[3]};
  61         152  
100              
101 61         270 return $made;
102             }
103              
104             sub undefer_all {
105 2     2 1 2139 undefer_sub($_) for keys %DEFERRED;
106 2         8 return;
107             }
108              
109             sub undefer_package {
110 4     4 1 33 my $package = shift;
111             undefer_sub($_)
112 4         24 for grep {
113 62   100     216 my $name = $DEFERRED{$_} && $DEFERRED{$_}[0];
114 62 100       452 $name && $name =~ /^${package}::[^:]+$/
115             } keys %DEFERRED;
116 4         18 return;
117             }
118              
119             sub defer_info {
120 26     26 1 7332 my ($deferred) = @_;
121 26 100 100     152 my $info = $DEFERRED{$deferred||''} or return undef;
122              
123 18         55 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
124 18 100 66     88 if (!(
      66        
      66        
125             $deferred_sub && $deferred eq $deferred_sub
126 12         36 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  6         33  
127             )) {
128 6         16 delete $DEFERRED{$deferred};
129 6         53 return undef;
130             }
131             [
132 12 100 66     123 $target, $maker, $options,
133             ( $undeferred_ref && $$undeferred_ref ? $$undeferred_ref : ()),
134             ];
135             }
136              
137             sub defer_sub {
138 98     98 1 274344 my ($target, $maker, $options) = @_;
139 98         180 my $package;
140             my $subname;
141 98 100 66     1118 ($package, $subname) = $target =~ /^(.*)::([^:]+)$/
142             or croak "$target is not a fully qualified sub name!"
143             if $target;
144 95   66     659 $package ||= $options && $options->{package} || caller;
      66        
145 95 100 100     144 my @attributes = @{$options && $options->{attributes} || []};
  95         544  
146 95 100       297 if (@attributes) {
147             /\A\w+(?:\(.*\))?\z/s || croak "invalid attribute $_"
148 7   66     431 for @attributes;
149             }
150 93         161 my $deferred;
151             my $undeferred;
152 93         252 my $deferred_info = [ $target, $maker, $options, \$undeferred ];
153 93 100 100     434 if (@attributes || $target && !_CAN_SUBNAME) {
      100        
154 15 100       98 my $code
    100          
155             = q[#line ].(__LINE__+2).q[ "].__FILE__.qq["\n]
156             . qq[package $package;\n]
157             . ($target ? "sub $subname" : '+sub') . join('', map " :$_", @attributes)
158             . q[ {
159             package Sub::Defer;
160             # uncoverable subroutine
161             # uncoverable statement
162             $undeferred ||= undefer_sub($deferred_info->[4]);
163             goto &$undeferred; # uncoverable statement
164             $undeferred; # fake lvalue return
165             }]."\n"
166             . ($target ? "\\&$subname" : '');
167 15         30 my $e;
168 15         21 $deferred = do {
169 13     13   131 no warnings qw(redefine closure);
  13         27  
  13         4547  
170 15         27 local $@;
171 15 50       1846 eval $code or $e = $@; # uncoverable branch true
172             };
173 15 50       52 die $e if defined $e; # uncoverable branch true
174             }
175             else {
176             # duplicated from above
177             $deferred = sub {
178 37   66 37   8707 $undeferred ||= undefer_sub($deferred_info->[4]);
179 36         798 goto &$undeferred;
180 78         320 };
181 78 100       226 _install_coderef($target, $deferred)
182             if $target;
183             }
184 93         231 weaken($deferred_info->[4] = $deferred);
185 93         349 weaken($DEFERRED{$deferred} = $deferred_info);
186 93         311 return $deferred;
187             }
188              
189             sub CLONE {
190             %DEFERRED = map {
191 10     10   123 defined $_ ? (
192             $_->[4] ? ($_->[4] => $_)
193 188 100 66     589 : ($_->[3] && ${$_->[3]}) ? (${$_->[3]} => $_)
  86 100       266  
    100          
194             : ()
195             ) : ()
196             } values %DEFERRED;
197             }
198              
199             1;
200             __END__