File Coverage

blib/lib/Attribute/Handlers/Clean.pm
Criterion Covered Total %
statement 364 376 96.8
branch 68 108 62.9
condition 27 49 55.1
subroutine 96 97 98.9
pod 1 1 100.0
total 556 631 88.1


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