| 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__ |