File Coverage

blib/lib/Devel/Isa/Explainer/_MRO.pm
Criterion Covered Total %
statement 119 123 96.7
branch 20 24 83.3
condition 6 11 54.5
subroutine 21 22 95.4
pod 9 9 100.0
total 175 189 92.5


line stmt bran cond sub pod time code
1 20     20   132844 use 5.006; # our
  20         46  
2 20     20   72 use strict;
  20         21  
  20         314  
3 20     20   54 use warnings;
  20         21  
  20         980  
4              
5             package Devel::Isa::Explainer::_MRO;
6              
7             # ABSTRACT: Method-resolution-order Utilities for DIE
8              
9             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
10              
11             our $VERSION = '0.002900'; # TRIAL
12              
13 20     20   3337 use MRO::Compat ();
  20         17712  
  20         256  
14 20     20   66 use Exporter ();
  20         22  
  20         283  
15 20     20   65 use Scalar::Util qw(reftype);
  20         21  
  20         1925  
16              
17             BEGIN {
18             ## no critic (ProhibitCallsToUnexportedSubs)
19 20     20   42 *import = \&Exporter::import;
20 20         24 *_mro_get_linear_isa = \&mro::get_linear_isa;
21 20         1292 *_mro_is_universal = \&mro::is_universal;
22             }
23              
24             # yes, this is evil
25              
26             our @EXPORT_OK = qw(
27             is_mro_proxy
28             get_linear_isa
29             get_package_sub
30             get_package_subs
31             get_linear_class_shadows
32             get_parents
33             get_linear_method_map
34             get_linear_class_map
35             get_flattened_class
36             );
37              
38             BEGIN {
39             # MRO Proxies removed since 5.009_005
40 20 50   20   387 *MRO_PROXIES = ( $] <= 5.009005 ) ? sub() { 1 } : sub() { 0 };
41             }
42              
43 20     20   7656 use namespace::clean -except => 'import';
  20         208631  
  20         86  
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61             sub is_mro_proxy {
62              
63             # Note: this sub should be optimised out from calling anyway
64             # but this is just a failsafe
65 0     0 1 0 MRO_PROXIES ? !!( $Class::C3::MRO{ $_[0] } || {} )->{methods}{ $_[1] } : 0;
66             }
67              
68              
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84             sub get_linear_isa {
85             [
86 40         436 @{ _mro_get_linear_isa( $_[0] ) },
87             #<<<
88             _mro_is_universal( $_[0] )
89             ? ()
90 40 100   40 1 98 : @{ _mro_get_linear_isa('UNIVERSAL') },
  37         149  
91             #>>>
92             ];
93             }
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106              
107              
108             sub get_package_sub {
109 55     55 1 440 return undef if MRO_PROXIES and is_mro_proxy(@_);
110 55         47 my ( $package, $sub ) = @_;
111              
112             # this is counter intuitive, but literally
113             # everything in a stash that is not a glob *is* a sub.
114             #
115             # Though they're usually constant-subs.
116             #
117             # Globs however can /contain/ subs in their {CODE} slot,
118             # but globs are not subs.
119 55         33 my $namespace = do {
120 20     20   4875 no strict 'refs';
  20         23  
  20         2480  
121 55         37 \%{"${package}::"};
  55         91  
122             };
123 55 100       156 return undef unless exists $namespace->{$sub};
124 25 50       85 if ( 'GLOB' eq reftype \$namespace->{$sub} ) {
125              
126             # Autoviv guard.
127 25 100       15 return defined *{ \$namespace->{$sub} }{'CODE'} ? *{ \$namespace->{$sub} }{'CODE'} : undef;
  25         60  
  21         53  
128             }
129              
130             # Note: This vivifies the stash slot into a glob...
131             # there's not much that can be done about this at present.
132             # Package::Stash does the same.
133             #
134             # This means the first of us or Package::Stash to traverse a symtable turns
135             # everything into globs in order to get coderefs out.
136             #
137             # Ideally, we don't do this, but ENEEDINFO
138 0         0 return \&{"${package}::${sub}"};
  0         0  
139             }
140              
141              
142              
143              
144              
145              
146              
147              
148              
149              
150              
151             # like get_package_sub, but does a whole class at once and returns a hashref
152             # of { name => CODEREF }
153             sub get_package_subs {
154 90     90 1 104 my ($package) = @_;
155 90         63 my ($namespace) = do {
156 20     20   79 no strict 'refs';
  20         48  
  20         843  
157 90         65 \%{"${package}::"};
  90         192  
158             };
159 90         78 my (@symnames) = do {
160 20     20   64 no strict 'refs';
  20         21  
  20         5360  
161 90         56 keys %{"${package}::"};
  90         470  
162             };
163 90         118 my $subs = {};
164 90         91 for my $symname (@symnames) {
165              
166 978         1363 my $reftype = reftype \$namespace->{$symname};
167              
168             # Globs are only subs if they contain a CODE slot
169             # all non-globs vivify to subs.
170             # Order can't be changed though, because the second test requires the
171             # first to be true to test, so defined is only tested when eq.
172 978 100 66     1315 next if ( 'GLOB' eq $reftype ) and not defined *{ \$namespace->{$symname} }{'CODE'};
  978         2469  
173 763         492 next if MRO_PROXIES and is_mro_proxy( $package, $symname );
174             $subs->{$symname} =
175             'GLOB' eq $reftype
176 763         1199 ? *{ \$namespace->{$symname} }{'CODE'}
177 763 50       754 : \&{"${package}::${symname}"};
  0         0  
178             }
179 90         232 $subs;
180             }
181              
182              
183              
184              
185              
186              
187              
188              
189              
190              
191              
192              
193              
194              
195              
196              
197              
198              
199              
200              
201             sub get_linear_class_shadows {
202 17     17 1 89 my ($class) = @_;
203              
204             # Contains the "image" made bottom up
205             # for comparison/detecting shadows.
206 17         51 my $methods = {};
207 17         21 my @isa_out;
208 17         16 for my $package ( reverse @{ get_linear_isa($class) } ) {
  17         41  
209 54         82 my $subs = get_package_subs($package);
210 54         47 my $node = {};
211 54         49 for my $subname ( keys %{$subs} ) {
  54         140  
212              
213             # first node is never shadowing
214 696 100       930 if ( not exists $methods->{$subname} ) {
215 682         1171 $node->{$subname} = { shadowing => 0, shadowed => 0, ref => $subs->{$subname} };
216              
217             # Contains a reference to the previous incarnation
218             # for later modification
219 682         598 $methods->{$subname} = $node->{$subname};
220 682         523 next;
221             }
222 14         29 $node->{$subname} = { shadowing => 1, shadowed => 0, ref => $subs->{$subname} };
223 14         18 $methods->{$subname}->{shadowed} = 1; # mark previous version shadowed
224 14         16 $methods->{$subname} = $node->{$subname}; # update current
225             }
226 54         227 unshift @isa_out, { class => $package, subs => $node };
227             }
228 17         139 \@isa_out;
229             }
230              
231              
232              
233              
234              
235              
236              
237              
238              
239              
240              
241              
242              
243              
244              
245              
246              
247              
248             sub get_parents {
249 39     39 1 39 my ($package) = @_;
250 39         25 my $namespace = do {
251 20     20   82 no strict 'refs';
  20         18  
  20         9161  
252 39         29 \%{"${package}::"};
  39         77  
253             };
254              
255 39 100       70 if ( exists $namespace->{ISA} ) {
256 11         15 my $entry_ref = \$namespace->{ISA};
257 11 50 33     36 if ( 'GLOB' eq reftype $entry_ref
      33        
258 11         39 and defined *{$entry_ref}{ARRAY}
259 11         10 and @{ *{$entry_ref}{ARRAY} } )
  11         35  
260             {
261 11         7 return [ @{ *{$entry_ref}{ARRAY} } ];
  11         81  
  11         39  
262             }
263             }
264 28 100       91 return [] if _mro_is_universal($package);
265 14         39 ['UNIVERSAL'];
266             }
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278              
279              
280              
281             sub get_linear_method_map {
282 9     9 1 2660 my ( $class, $method ) = @_;
283 9         8 return [ map { [ $_, get_package_sub( $_, $method ) ] } @{ get_linear_isa($class) } ];
  45         47  
  9         17  
284             }
285              
286              
287              
288              
289              
290              
291              
292              
293              
294              
295              
296              
297              
298              
299              
300              
301             sub get_linear_class_map {
302 3     3 1 65 my ($class) = @_;
303 3         4 [ map { [ $_, get_package_subs($_) ] } @{ get_linear_isa($class) } ];
  15         40  
  3         5  
304             }
305              
306              
307              
308              
309              
310              
311              
312              
313              
314              
315              
316              
317              
318              
319              
320              
321              
322              
323              
324              
325             sub get_flattened_class {
326 3     3 1 11982 my ($class) = @_;
327 3         32 my $methods = {};
328 3         4 for my $package ( reverse @{ get_linear_isa($class) } ) {
  3         5  
329 15         19 my $subs = get_package_subs($package);
330 15         9 for my $subname ( keys %{$subs} ) {
  15         24  
331 23   100     57 $methods->{$subname}->{parents} ||= [];
332 5         11 unshift @{ $methods->{$subname}->{parents} }, [ $methods->{$subname}->{via}, $methods->{$subname}->{ref} ]
333 23 100       32 if exists $methods->{$subname}->{ref};
334 23         20 $methods->{$subname}->{ref} = $subs->{$subname};
335 23         29 $methods->{$subname}->{via} = $package;
336             }
337             }
338 3         43 $methods;
339             }
340              
341             1;
342              
343             __END__