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   432684 use strict;
  13         74  
  13         380  
3 13     13   71 use warnings;
  13         33  
  13         732  
4              
5             our $VERSION = '2.006008';
6             $VERSION =~ tr/_//d;
7              
8 13     13   77 use Exporter ();
  13         27  
  13         416  
9 13     13   375 BEGIN { *import = \&Exporter::import }
10 13     13   74 use Scalar::Util qw(weaken);
  13         33  
  13         901  
11 13     13   91 use Carp qw(croak);
  13         22  
  13         1215  
12              
13             our @EXPORT = qw(defer_sub undefer_sub undefer_all);
14             our @EXPORT_OK = qw(undefer_package defer_info);
15              
16 13     13   92 sub _getglob { no strict 'refs'; \*{$_[0]} }
  13     84   20  
  13         2963  
  84         101  
  84         400  
17              
18             BEGIN {
19 13     13   54 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   97 : ($no_subname = 1, sub { $_[1] });
  1 100 66     540  
    100          
    100          
26 13 100       3739 *_CAN_SUBNAME = $no_subname ? sub(){0} : sub(){1};
27             }
28              
29             sub _name_coderef {
30 26 100   26   62 shift if @_ > 2; # three args is (target, name, sub)
31 26         164 _CAN_SUBNAME ? _subname(@_) : $_[1];
32             }
33              
34             sub _install_coderef {
35 26     26   1138 my ($glob, $code) = (_getglob($_[0]), _name_coderef(@_));
36 13     13   110 no warnings 'redefine';
  13         26  
  13         855  
37 26 100       45 if (*{$glob}{CODE}) {
  26         64  
38 3         9 *{$glob} = $code;
  3         10  
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   99 no warnings 'prototype';
  13         45  
  13         2860  
44 23         31 *{$glob} = $code;
  23         62  
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 3582 my ($deferred) = @_;
75 98 100       310 my $info = $DEFERRED{$deferred} or return $deferred;
76 86         199 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
77              
78 86 100 66     326 if (!(
      66        
      100        
79             $deferred_sub && $deferred eq $deferred_sub
80 14         34 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  12         42  
81             )) {
82 2         9 return $deferred;
83             }
84              
85 22         78 return ${$undeferred_ref}
86 84 100       118 if ${$undeferred_ref};
  84         167  
87 62         130 ${$undeferred_ref} = my $made = $maker->();
  61         228  
88              
89             # make sure the method slot has not changed since deferral time
90 61 100 100     221 if (defined($target) && $deferred eq *{_getglob($target)}{CODE}||'') {
  30   100     55  
91 13     13   89 no warnings 'redefine';
  13         43  
  13         9627  
92              
93             # I believe $maker already evals with the right package/name, so that
94             # _install_coderef calls are not necessary --ribasushi
95 28         40 *{_getglob($target)} = $made;
  28         47  
96             }
97 61         162 my $undefer_info = [ $target, $maker, $options, $undeferred_ref ];
98 61         198 $info->[5] = $DEFERRED{$made} = $undefer_info;
99 61         92 weaken ${$undefer_info->[3]};
  61         183  
100              
101 61         262 return $made;
102             }
103              
104             sub undefer_all {
105 2     2 1 1440 undefer_sub($_) for keys %DEFERRED;
106 2         6 return;
107             }
108              
109             sub undefer_package {
110 4     4 1 25 my $package = shift;
111             undefer_sub($_)
112 4         17 for grep {
113 62   100     162 my $name = $DEFERRED{$_} && $DEFERRED{$_}[0];
114 62 100       340 $name && $name =~ /^${package}::[^:]+$/
115             } keys %DEFERRED;
116 4         14 return;
117             }
118              
119             sub defer_info {
120 26     26 1 5085 my ($deferred) = @_;
121 26 100 100     130 my $info = $DEFERRED{$deferred||''} or return undef;
122              
123 18         43 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
124 18 100 66     76 if (!(
      66        
      66        
125             $deferred_sub && $deferred eq $deferred_sub
126 12         30 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  6         25  
127             )) {
128 6         32 delete $DEFERRED{$deferred};
129 6         39 return undef;
130             }
131             [
132 12 100 66     115 $target, $maker, $options,
133             ( $undeferred_ref && $$undeferred_ref ? $$undeferred_ref : ()),
134             ];
135             }
136              
137             sub defer_sub {
138 98     98 1 24883 my ($target, $maker, $options) = @_;
139 98         153 my $package;
140             my $subname;
141 98 100 66     1055 ($package, $subname) = $target =~ /^(.*)::([^:]+)$/
142             or croak "$target is not a fully qualified sub name!"
143             if $target;
144 95   66     494 $package ||= $options && $options->{package} || caller;
      66        
145 95 100 100     136 my @attributes = @{$options && $options->{attributes} || []};
  95         445  
146 95 100       221 if (@attributes) {
147             /\A\w+(?:\(.*\))?\z/s || croak "invalid attribute $_"
148 7   66     247 for @attributes;
149             }
150 93         147 my $deferred;
151             my $undeferred;
152 93         229 my $deferred_info = [ $target, $maker, $options, \$undeferred ];
153 93 100 100     360 if (@attributes || $target && !_CAN_SUBNAME) {
      100        
154 15 100       95 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         29 my $e;
168 15         34 $deferred = do {
169 13     13   120 no warnings qw(redefine closure);
  13         36  
  13         4198  
170 15         24 local $@;
171 15 50       1483 eval $code or $e = $@; # uncoverable branch true
172             };
173 15 50       47 die $e if defined $e; # uncoverable branch true
174             }
175             else {
176             # duplicated from above
177             $deferred = sub {
178 37   66 37   5747 $undeferred ||= undefer_sub($deferred_info->[4]);
179 36         782 goto &$undeferred;
180 78         272 };
181 78 100       193 _install_coderef($target, $deferred)
182             if $target;
183             }
184 93         314 weaken($deferred_info->[4] = $deferred);
185 93         373 weaken($DEFERRED{$deferred} = $deferred_info);
186 93         268 return $deferred;
187             }
188              
189             sub CLONE {
190             %DEFERRED = map {
191 10     10   98 defined $_ ? (
192             $_->[4] ? ($_->[4] => $_)
193 188 100 66     463 : ($_->[3] && ${$_->[3]}) ? (${$_->[3]} => $_)
  86 100       180  
    100          
194             : ()
195             ) : ()
196             } values %DEFERRED;
197             }
198              
199             1;
200             __END__