File Coverage

blib/lib/Attribute/Handlers/Clean.pm
Criterion Covered Total %
statement 358 368 97.2
branch 69 108 63.8
condition 28 49 57.1
subroutine 94 95 98.9
pod 1 1 100.0
total 550 621 88.5


line stmt bran cond sub pod time code
1             package Attribute::Handlers::Clean;
2 4     4   208590 use 5.006;
  4         36  
3 4     4   23 use Carp;
  4         8  
  4         284  
4 4     4   26 use warnings;
  4         7  
  4         144  
5 4     4   23 use strict;
  4         8  
  4         450  
6            
7             our $VERSION = '1.02'; # remember to update version in POD!
8             our $AUTOLOAD;
9            
10             my %symcache;
11             sub findsym {
12 74     74 1 154 my ($pkg, $ref, $type) = @_;
13 74 100       278 return $symcache{$pkg,$ref} if $symcache{$pkg,$ref};
14 73   33     334 $type ||= ref($ref);
15 4     4   29 no strict 'refs';
  4         6  
  4         424  
16 73         111 my $symtab = \%{$pkg."::"};
  73         238  
17 73         1861 for (keys %$symtab) {
18 5393         9249 for my $sym ($$symtab{$_}) {
19 5393 100 100     9772 if (ref $sym && $sym == $ref) {
20 8         16 return $symcache{$pkg,$ref} = \*{"$pkg:\:$_"};
  8         125  
21             }
22 4     4   25 use strict;
  4         8  
  4         1576  
23 5385 100       9600 next unless ref (\$sym) eq 'GLOB';
24             return $symcache{$pkg,$ref} = \$sym
25 5371 100 100     7048 if *{$sym}{$type} && *{$sym}{$type} == $ref;
  5371         16231  
  2660         11551  
26             }
27             }
28             }
29            
30             my %validtype = (
31             VAR => [qw[SCALAR ARRAY HASH]],
32             ANY => [qw[SCALAR ARRAY HASH CODE]],
33             "" => [qw[SCALAR ARRAY HASH CODE]],
34             SCALAR => [qw[SCALAR]],
35             ARRAY => [qw[ARRAY]],
36             HASH => [qw[HASH]],
37             CODE => [qw[CODE]],
38             );
39             my %lastattr;
40             my @declarations;
41             my %raw;
42             my %phase;
43             my %sigil = (SCALAR=>'$', ARRAY=>'@', HASH=>'%');
44             my $global_phase = 0;
45             my %global_phases = (
46             BEGIN => 0,
47             CHECK => 1,
48             INIT => 2,
49             END => 3,
50             );
51             my @global_phases = qw(BEGIN CHECK INIT END);
52            
53             sub _usage_AH_ {
54 0     0   0 croak "Usage: use $_[0] autotie => {AttrName => TieClassName,...}";
55             }
56            
57             my $qual_id = qr/^[_a-z]\w*(::[_a-z]\w*)*$/i;
58            
59             sub import {
60 7     7   1096 my $class = shift @_;
61             {
62 7         14 my @caller_0 = caller 0;
  7         42  
63 7         33 my @caller_1 = caller 1;
64             # Allow just calling "package->import" inside of subclses own import method, to mean their caller should get the same import effect from this module - in the same style of warnings->unimport
65 7 50 33     55 my $caller = (@caller_1 and $caller_1[3] eq "$caller_0[0]::import") ? $caller_1[0] : $caller_0[0];
66 4     4   31 no strict 'refs';
  4         9  
  4         2466  
67 7         95 warnings->unimport('reserved');
68 7 100 66     33 push (@{"${caller}::ISA"}, $class) unless $caller eq $class or grep {$_ eq $class} @{"${caller}::ISA"};
  6         88  
  3         12  
  7         44  
69 7         16 for (@{$validtype{ANY}}) {
  7         33  
70 28 100       42 *{"${caller}::MODIFY_${_}_ATTRIBUTES"} = _gen_handler_AH_() unless defined *{"${caller}::MODIFY_${_}_ATTRIBUTES"};
  20         94  
  28         130  
71             }
72             }
73 7 100       105 return unless $class eq __PACKAGE__;
74 6         538 while (@_) {
75 1         3 my $cmd = shift;
76 1 50       8 if ($cmd =~ /^autotie((?:ref)?)$/) {
77 1 50       5 my $tiedata = ($1 ? '$ref, ' : '') . '@$data';
78 1         2 my $mapping = shift;
79 1 50       13 _usage_AH_ $class unless ref($mapping) eq 'HASH';
80 1         7 while (my($attr, $tieclass) = each %$mapping) {
81 3         24 $tieclass =~ s/^([_a-z]\w*(::[_a-z]\w*)*)(.*)/$1/is;
82 3   50     23 my $args = $3||'()';
83 3 50 33 1   241 _usage_AH_ $class unless $attr =~ $qual_id && $tieclass =~ $qual_id && eval "use base q\0$tieclass\0; 1";
  1   33 1   9  
  1     1   3  
  1         45  
  1         45  
  1         7  
  1         2  
  1         3  
  1         38  
  1         8  
84 3 50       25 if ($tieclass->isa('Exporter')) {
85 0         0 local $Exporter::ExportLevel = 2;
86 0         0 $tieclass->import(eval $args);
87             }
88 3         9 $attr =~ s/__CALLER__/caller(1)/e;
  0         0  
89 3 100       13 $attr = caller()."::".$attr unless $attr =~ /::/;
90 3 50       540 eval '# line '.__LINE__.' "'.__FILE__.qq{"
91 1         7 sub $attr : ATTR(VAR) {
  1         77  
  1         2  
92 1         3 my (\$ref, \$data) = \@_[2,4];
  1         7  
  1         50  
93 1 50       43 my \$was_arrayref = ref \$data eq 'ARRAY';
  1 50       3  
  1 50       7  
94 1   33     6 \$data = [ \$data ] unless \$was_arrayref;
  1   33     30  
  1   33     2  
95 1 50       2 my \$type = ref(\$ref)||"value (".(\$ref||"").")";
  1 50       6  
  1 50       31  
    0          
    50          
    50          
    0          
    0          
    50          
96             (\$type eq 'SCALAR') ?
97             tie \$\$ref,'$tieclass',$tiedata :
98             (\$type eq 'ARRAY') ?
99             tie \@\$ref,'$tieclass',$tiedata :
100             (\$type eq 'HASH') ?
101             tie \%\$ref,'$tieclass',$tiedata :
102             die "Can't autotie a \$type\n";
103 1     1   7 } 1
  1     1   2  
  1     1   44  
  1         43  
  1         8  
  1         2  
  1         2  
  1         31  
  1         6  
104             } or die "Internal error: $@";
105             }
106             } else {
107 0         0 croak "Can't understand $_";
108             }
109             }
110             }
111            
112             # On older perls, code attribute handlers run before the sub gets placed
113             # in its package. Since the :ATTR handlers need to know the name of the
114             # sub they're applied to, the name lookup (via findsym) needs to be
115             # delayed: we do it immediately before we might need to find attribute
116             # handlers from their name. However, on newer perls (which fix some
117             # problems relating to attribute application), a sub gets placed in its
118             # package before its attributes are processed. In this case, the
119             # delayed name lookup might be too late, because the sub we're looking
120             # for might have already been replaced. So we need to detect which way
121             # round this perl does things, and time the name lookup accordingly.
122             BEGIN {
123 4     4   773 my $delayed;
124             sub Attribute::Handlers::Clean::_TEST_::MODIFY_CODE_ATTRIBUTES {
125             $delayed = \&Attribute::Handlers::Clean::_TEST_::t != $_[1];
126             return ();
127             }
128 4     4   2343 sub Attribute::Handlers::Clean::_TEST_::t :T { }
  4         4940  
  4         24  
129 4     100   27 *_delayed_name_resolution = sub() { $delayed };
  99         303  
130 4         32 undef &Attribute::Handlers::Clean::_TEST_::MODIFY_CODE_ATTRIBUTES;
131 4         441 undef &Attribute::Handlers::Clean::_TEST_::t;
132             }
133            
134             sub _resolve_lastattr {
135 21 50   22   47 return unless $lastattr{ref};
136 21 50       62 my $sym = findsym @lastattr{'pkg','ref'} or die "Internal error: $lastattr{pkg} symbol went missing";
137 21         90 my $name = *{$sym}{NAME};
  21         52  
138 21 50 66     94 warn "Declaration of $name attribute in package $lastattr{pkg} may clash with future reserved word\n" if $^W and $name !~ /[A-Z]/;
139 21         33 foreach ( @{$validtype{$lastattr{type}}} ) {
  21         68  
140 4     4   29 no strict 'refs';
  4         8  
  4         1713  
141 47         75 *{"$lastattr{pkg}::_ATTR_${_}_${name}"} = $lastattr{ref};
  47         227  
142             }
143 21         73 %lastattr = ();
144             }
145            
146             sub AUTOLOAD {
147 0 0   1   0 return if $AUTOLOAD =~ /::DESTROY$/;
148 0         0 my ($class) = $AUTOLOAD =~ m/(.*)::/g;
149 0 0       0 $AUTOLOAD =~ m/_ATTR_(.*?)_(.*)/ or croak "Can't locate class method '$AUTOLOAD' via package '$class'";
150 0         0 croak "Attribute handler '$2' doesn't handle $1 attributes";
151             }
152            
153             my $builtin = qr/lvalue|method|locked|unique|shared/;
154            
155             sub _gen_handler_AH_() {
156             sub {
157 74 50   74   17096 _resolve_lastattr if _delayed_name_resolution;
158 74         208 my ($pkg, $ref, @attrs) = @_;
159 74         287 my (undef, $filename, $linenum) = caller 2;
160 74         183 foreach (@attrs) {
161 75 50       618 my ($attr, $data) = /^([a-z_]\w*)(?:[(](.*)[)])?$/is or next;
162 75 100       230 if ($attr eq 'ATTR') {
163 4     4   34 no strict 'refs';
  4         7  
  4         2624  
164 21   100     53 $data ||= "ANY";
165 21         79 $raw{$ref} = $data =~ s/\s*,?\s*RAWDATA\s*,?\s*//;
166 21 100       84 $phase{$ref}{BEGIN} = 1 if $data =~ s/\s*,?\s*(BEGIN)\s*,?\s*//;
167 21 100       58 $phase{$ref}{INIT} = 1 if $data =~ s/\s*,?\s*(INIT)\s*,?\s*//;
168 21 100       56 $phase{$ref}{END} = 1 if $data =~ s/\s*,?\s*(END)\s*,?\s*//;
169 21 100 100     75 $phase{$ref}{CHECK} = 1 if $data =~ s/\s*,?\s*(CHECK)\s*,?\s*// || ! keys %{$phase{$ref}};
  20         156  
170             # Added for cleanup to not pollute next call.
171 21 50       80 (%lastattr = ()), croak "Can't have two ATTR specifiers on one subroutine" if keys %lastattr;
172 21 50       61 croak "Bad attribute type: ATTR($data)" unless $validtype{$data};
173 21         76 %lastattr=(pkg=>$pkg,ref=>$ref,type=>$data);
174 21 50       80 _resolve_lastattr unless _delayed_name_resolution;
175             } else {
176 54         117 my $type = ref $ref;
177 54         342 my $handler = $pkg->can("_ATTR_${type}_${attr}");
178 54 50       143 next unless $handler;
179 54         228 my $decl = [$pkg, $ref, $attr, $data, $raw{$handler}, $phase{$handler}, $filename, $linenum];
180 54         112 foreach my $gphase (@global_phases) {
181             _apply_handler_AH_($decl,$gphase)
182 216 100       579 if $global_phases{$gphase} <= $global_phase;
183             }
184 54 100       119 if ($global_phase != 0) {
185             # if _gen_handler_AH_ is being called after
186             # CHECK it's for a lexical, so make sure
187             # it didn't want to run anything later
188 35         82 local $Carp::CarpLevel = 2;
189 35 100       475 carp "Won't be able to apply END handler" if $phase{$handler}{END};
190             } else {
191 19         99 push @declarations, $decl
192             }
193             }
194 74         168 $_ = undef;
195             }
196 73 50       139 return grep {defined && !/$builtin/} @attrs;
  74         384  
197             }
198 20     20   102 }
199            
200             sub _apply_handler_AH_ {
201 181     181   340 my ($declaration, $phase) = @_;
202 181         445 my ($pkg, $ref, $attr, $data, $raw, $handlerphase, $filename, $linenum) = @$declaration;
203 181 100       1500 return unless $handlerphase->{$phase};
204             # print STDERR "Handling $attr on $ref in $phase with [$data]\n";
205 53         109 my $type = ref $ref;
206 53         128 my $handler = "_ATTR_${type}_${attr}";
207 53         122 my $sym = findsym($pkg, $ref);
208 53 50 66     441 $sym ||= $type eq 'CODE' ? 'ANON' : 'LEXICAL';
209 4     4   37 no warnings;
  4         8  
  4         663  
210 53 100 66     190 if (!$raw && defined($data)) {
211 45 50       103 if ($data ne '') {
212 45     4   3755 my $evaled = eval(
  4     4   31  
  4     4   8  
  4     4   513  
  4     3   25  
  4     3   8  
  4     2   165  
  4     2   27  
  4     2   11  
  4     2   117  
  4     2   30  
  4     2   19  
  4     2   165  
  3     2   17  
  3     2   333  
  3     2   85  
  3     1   2080  
  3     1   10  
  3     1   115  
  3     1   18  
  3     1   8  
  3     1   67  
  3     1   20  
  3     1   33  
  3     1   111  
  3     1   346  
  3     1   13  
  3     1   66  
  3     1   15  
  3     1   9  
  3     1   103  
  3     1   17  
  3     1   10  
  3     1   88  
  2     1   14  
  2     1   4  
  2     1   94  
  2     1   15  
  2     1   6  
  2     1   81  
  2     1   41  
  2     1   5  
  2     1   95  
  2     1   34  
  2     1   7  
  2     1   83  
  2     1   12  
  2     1   4  
  2     1   93  
  1     1   6  
  1     1   2  
  1     1   40  
  1     1   7  
  1     1   2  
  1     1   31  
  1     1   6  
  1     1   2  
  1     1   53  
  1     1   7  
  1     1   2  
  1     1   30  
  1     1   6  
  1     1   1  
  1     1   53  
  1         7  
  1         2  
  1         33  
  1         5  
  1         2  
  1         52  
  1         7  
  1         3  
  1         31  
  1         5  
  1         2  
  1         51  
  1         8  
  1         2  
  1         34  
  1         6  
  1         2  
  1         51  
  1         7  
  1         3  
  1         30  
  1         5  
  1         2  
  1         49  
  1         8  
  1         2  
  1         31  
  1         6  
  1         2  
  1         38  
  1         7  
  1         2  
  1         43  
  1         6  
  1         2  
  1         42  
  1         6  
  1         3  
  1         30  
  1         6  
  1         3  
  1         38  
  1         7  
  1         3  
  1         49  
  1         7  
  1         2  
  1         40  
  1         7  
  1         2  
  1         44  
  1         7  
  1         3  
  1         39  
  1         8  
  1         3  
  1         43  
  1         7  
  1         2  
  1         41  
  1         8  
  1         2  
  1         41  
  1         7  
  1         2  
  1         42  
  1         7  
  1         3  
  1         29  
  1         5  
  1         2  
  1         52  
  1         7  
  1         2  
  1         29  
  1         6  
  1         2  
  1         53  
  1         6  
  1         64  
  1         36  
  1         45  
  1         4  
  1         45  
  1         7  
  1         2  
  1         40  
  1         7  
  1         3  
  1         40  
  1         7  
  1         1  
  1         31  
  1         5  
  1         2  
  1         49  
  1         7  
  1         2  
  1         31  
  1         6  
  1         2  
  1         51  
  1         8  
  1         3  
  1         33  
  1         6  
  1         2  
  1         43  
  1         7  
  1         3  
  1         30  
  1         6  
  1         2  
  1         56  
213             "
214             package $pkg;
215             no warnings;
216             no strict;
217             local \$SIG{__WARN__}=sub{die};
218             [$data]
219             "
220             );
221 45 100       190 $data = $evaled unless $@;
222             } else {
223 0         0 $data = undef;
224             }
225             }
226             $pkg->$handler(
227             $sym,
228 53 100 33     291 (ref $sym eq 'GLOB' ? *{$sym}{ref $ref} || $ref : $ref),
229             $attr,
230             $data,
231             $phase,
232             $filename,
233             $linenum,
234             );
235 53         16131 return 1;
236             }
237            
238             {
239 4     4   32 no warnings 'void';
  4         8  
  4         916  
240             CHECK {
241 4     4   3014 $global_phase++;
242 4 50       14 _resolve_lastattr if _delayed_name_resolution;
243 4         617 foreach my $decl (@declarations) {
244 19         44 _apply_handler_AH_($decl, 'CHECK');
245             }
246             }
247             INIT {
248 4     4   250 $global_phase++;
249 4         17 foreach my $decl (@declarations) {
250 19         48 _apply_handler_AH_($decl, 'INIT');
251             }
252             }
253             }
254            
255             END {
256 4     4   2054 $global_phase++;
257 4         45 foreach my $decl (@declarations) {
258 19         36 _apply_handler_AH_($decl, 'END');
259             }
260             }
261            
262             1;
263            
264             __END__