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 14     14   346087 use strict;
  14         117  
  14         345  
3 14     14   61 use warnings;
  14         19  
  14         559  
4              
5             our $VERSION = '2.006_007';
6             $VERSION =~ tr/_//d;
7              
8 14     14   65 use Exporter ();
  14         23  
  14         336  
9 14     14   263 BEGIN { *import = \&Exporter::import }
10 14     14   77 use Scalar::Util qw(weaken);
  14         36  
  14         776  
11 14     14   72 use Carp qw(croak);
  14         23  
  14         1032  
12              
13             our @EXPORT = qw(defer_sub undefer_sub undefer_all);
14             our @EXPORT_OK = qw(undefer_package defer_info);
15              
16 14     14   89 sub _getglob { no strict 'refs'; \*{$_[0]} }
  14     84   23  
  14         2390  
  84         88  
  84         324  
17              
18             BEGIN {
19 14     14   43 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 14 100 66 31   79 : ($no_subname = 1, sub { $_[1] });
  1 100 66     449  
    100          
    100          
26 14 100       3081 *_CAN_SUBNAME = $no_subname ? sub(){0} : sub(){1};
27             }
28              
29             sub _name_coderef {
30 26 100   26   55 shift if @_ > 2; # three args is (target, name, sub)
31 26         129 _CAN_SUBNAME ? _subname(@_) : $_[1];
32             }
33              
34             sub _install_coderef {
35 26     26   820 my ($glob, $code) = (_getglob($_[0]), _name_coderef(@_));
36 14     14   81 no warnings 'redefine';
  14         27  
  14         725  
37 26 100       36 if (*{$glob}{CODE}) {
  26         54  
38 3         4 *{$glob} = $code;
  3         9  
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 14     14   75 no warnings 'prototype';
  14         21  
  14         2460  
44 23         27 *{$glob} = $code;
  23         50  
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 3018 my ($deferred) = @_;
75 98 100       234 my $info = $DEFERRED{$deferred} or return $deferred;
76 86         158 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
77              
78 86 100 66     277 if (!(
      66        
      100        
79             $deferred_sub && $deferred eq $deferred_sub
80 14         27 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  12         33  
81             )) {
82 2         5 return $deferred;
83             }
84              
85 22         69 return ${$undeferred_ref}
86 84 100       87 if ${$undeferred_ref};
  84         139  
87 62         109 ${$undeferred_ref} = my $made = $maker->();
  61         185  
88              
89             # make sure the method slot has not changed since deferral time
90 61 100 100     178 if (defined($target) && $deferred eq *{_getglob($target)}{CODE}||'') {
  30   100     43  
91 14     14   92 no warnings 'redefine';
  14         27  
  14         7940  
92              
93             # I believe $maker already evals with the right package/name, so that
94             # _install_coderef calls are not necessary --ribasushi
95 28         38 *{_getglob($target)} = $made;
  28         41  
96             }
97 61         114 my $undefer_info = [ $target, $maker, $options, $undeferred_ref ];
98 61         163 $info->[5] = $DEFERRED{$made} = $undefer_info;
99 61         77 weaken ${$undefer_info->[3]};
  61         156  
100              
101 61         187 return $made;
102             }
103              
104             sub undefer_all {
105 2     2 1 1150 undefer_sub($_) for keys %DEFERRED;
106 2         5 return;
107             }
108              
109             sub undefer_package {
110 4     4 1 21 my $package = shift;
111             undefer_sub($_)
112 4         14 for grep {
113 62   100     137 my $name = $DEFERRED{$_} && $DEFERRED{$_}[0];
114 62 100       256 $name && $name =~ /^${package}::[^:]+$/
115             } keys %DEFERRED;
116 4         10 return;
117             }
118              
119             sub defer_info {
120 26     26 1 4073 my ($deferred) = @_;
121 26 100 100     95 my $info = $DEFERRED{$deferred||''} or return undef;
122              
123 18         37 my ($target, $maker, $options, $undeferred_ref, $deferred_sub) = @$info;
124 18 100 66     51 if (!(
      66        
      66        
125             $deferred_sub && $deferred eq $deferred_sub
126 12         27 || ${$undeferred_ref} && $deferred eq ${$undeferred_ref}
  6         20  
127             )) {
128 6         9 delete $DEFERRED{$deferred};
129 6         31 return undef;
130             }
131             [
132 12 100 66     77 $target, $maker, $options,
133             ( $undeferred_ref && $$undeferred_ref ? $$undeferred_ref : ()),
134             ];
135             }
136              
137             sub defer_sub {
138 98     98 1 19971 my ($target, $maker, $options) = @_;
139 98         121 my $package;
140             my $subname;
141 98 100 66     756 ($package, $subname) = $target =~ /^(.*)::([^:]+)$/
142             or croak "$target is not a fully qualified sub name!"
143             if $target;
144 95   66     410 $package ||= $options && $options->{package} || caller;
      66        
145 95 100 100     108 my @attributes = @{$options && $options->{attributes} || []};
  95         359  
146 95 100       185 if (@attributes) {
147             /\A\w+(?:\(.*\))?\z/s || croak "invalid attribute $_"
148 7   66     223 for @attributes;
149             }
150 93         125 my $deferred;
151             my $undeferred;
152 93         170 my $deferred_info = [ $target, $maker, $options, \$undeferred ];
153 93 100 100     310 if (@attributes || $target && !_CAN_SUBNAME) {
      100        
154 15 100       83 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         18 my $e;
168 15         17 $deferred = do {
169 14     14   97 no warnings qw(redefine closure);
  14         18  
  14         3541  
170 15         27 local $@;
171 15 50       1158 eval $code or $e = $@; # uncoverable branch true
172             };
173 15 50       40 die $e if defined $e; # uncoverable branch true
174             }
175             else {
176             # duplicated from above
177             $deferred = sub {
178 37   66 37   5402 $undeferred ||= undefer_sub($deferred_info->[4]);
179 36         641 goto &$undeferred;
180 78         212 };
181 78 100       179 _install_coderef($target, $deferred)
182             if $target;
183             }
184 93         270 weaken($deferred_info->[4] = $deferred);
185 93         275 weaken($DEFERRED{$deferred} = $deferred_info);
186 93         228 return $deferred;
187             }
188              
189             sub CLONE {
190             %DEFERRED = map {
191 10     10   231 defined $_ ? (
192             $_->[4] ? ($_->[4] => $_)
193 188 100 66     375 : ($_->[3] && ${$_->[3]}) ? (${$_->[3]} => $_)
  86 100       132  
    100          
194             : ()
195             ) : ()
196             } values %DEFERRED;
197             }
198              
199             1;
200             __END__