File Coverage

blib/lib/Class/Multimethods.pm
Criterion Covered Total %
statement 218 639 34.1
branch 54 304 17.7
condition 53 193 27.4
subroutine 22 35 62.8
pod 1 17 5.8
total 348 1188 29.2


line stmt bran cond sub pod time code
1             package Class::Multimethods;
2              
3 1     1   854 use strict;
  1         2  
  1         29  
4 1     1   4 use vars qw($VERSION @ISA @EXPORT);
  1         1  
  1         97  
5 1     1   5 use Carp;
  1         6  
  1         115  
6              
7             our $VERSION = '1.701';
8              
9             require Exporter;
10             @ISA = qw(Exporter);
11             @EXPORT = qw( multimethod resolve_ambiguous resolve_no_match superclass multimethod_wrapper );
12              
13 1     1   4 use vars qw(%dispatch %cached %hasgeneric %ambiguous_handler %no_match_handler %max_args %min_args %dispatch_installed);
  1         1  
  1         880  
14              
15             %dispatch = (); # THE DISPATCH TABLE
16             %cached = (); # THE CACHE OF PREVIOUS RESOLUTIONS OF EMPTY SLOTS
17             %hasgeneric = (); # WHETHER A GIVEN MULTIMETHOD HAS ANY GENERIC VARIANTS
18             %ambiguous_handler = (); # HANDLERS FOR AMBIGUOUS CALLS
19             %no_match_handler = (); # HANDLERS FOR AMBIGUOUS CALLS
20             %max_args = (); # RECORDS MAX NUM OF ARGS IN ANY VARIANT
21             %min_args = (); # RECORDS MIN NUM OF ARGS IN ANY VARIANT
22              
23             %dispatch_installed = (); # RECORDS DISPATCHES ALREADY INSTALLED __BY__ __US__
24              
25             # THIS IS INTERPOSED BETWEEN THE CALLING PACKAGE AND Exporter TO SUPPORT THE
26             # use Class:Multimethods @methodnames SYNTAX
27              
28             sub import
29             {
30 5     5   36 my $package = (caller)[0];
31 5         22 install_dispatch($package,pop @_) while $#_;
32 5         4255 Class::Multimethods->export_to_level(1);
33             }
34              
35              
36             # INSTALL A DISPATCHING SUB FOR THE NAMED MULTIMETHOD IN THE CALLING PACKAGE
37              
38             sub install_dispatch
39             {
40 28     28 0 32 my ($pkg, $name) = @_;
41             # eval "sub ${pkg}::$name { Class::Multimethods::dispatch('$name',\@_) }"
42 28 100       76 if ( ! $dispatch_installed{$pkg}{$name} )
43             {
44 8 50 66 1   19 eval(make_dispatch($pkg,$name)) || croak "internal error: $@";
  1 50 33 2   38  
  1 0 0 252   2  
  1 0 0 4   3  
  2 0 0 3   9  
  2 0 66 2   2  
  2 0 33 4   4  
  1 0 0 81   1  
  1 0 0     6  
  1 0 0     7  
  0 0 66     0  
  0 0 66     0  
  0 0 100     0  
  0 50 66     0  
  0 0 0     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 66     0  
  0 0 33     0  
  0 0 66     0  
  0 0 33     0  
  0 0 0     0  
  0 100 66     0  
  0 50 33     0  
  0 0 0     0  
  0 50 0     0  
  0 0 0     0  
  0 0 33     0  
  0 50 66     0  
  0 0 66     0  
  0 0 33     0  
  0 50 0     0  
  0 100 33     0  
  0 100 66     0  
  0 50 100     0  
  0 0 66     0  
  0 0 66     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  2 0       80  
  2 0       3  
  2 0       3  
  4 50       15  
  4 50       4  
  4 0       7  
  2 0       2  
  2 0       6  
  2 0       7  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 100       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 100       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  252 50       38969  
  252 0       281  
  252 0       484  
  508 0       892  
  508 100       481  
  508 50       530  
  252         268  
  252         698  
  252         442  
  220         541  
  220         475  
  222         744  
  0         0  
  220         309  
  220         170  
  220         450  
  220         555  
  220         641  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  220         436  
  25         53  
  25         54  
  12         18  
  12         24  
  0         0  
  0         0  
  12         23  
  12         37  
  24         1031  
  183         202  
  183         282  
  0         0  
  0         0  
  183         374  
  183         15120  
  4         209  
  4         4  
  4         6  
  8         33  
  8         8  
  8         9  
  4         5  
  4         9  
  4         13  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         167  
  3         3  
  3         6  
  6         20  
  6         6  
  6         7  
  3         3  
  3         9  
  3         5  
  3         7  
  3         8  
  3         11  
  0         0  
  3         5  
  3         3  
  3         8  
  3         7  
  3         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         6  
  3         4  
  3         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         83  
  2         2  
  2         4  
  4         12  
  4         4  
  4         5  
  2         2  
  2         6  
  2         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         506  
  4         4  
  4         9  
  8         36  
  8         9  
  8         9  
  4         5  
  4         13  
  4         13  
  1         3  
  1         3  
  1         5  
  0         0  
  1         2  
  1         1  
  1         2  
  1         4  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         3  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  81         17205  
  81         82  
  81         153  
  162         310  
  162         143  
  162         181  
  81         96  
  81         220  
  81         140  
  79         187  
  79         183  
  79         275  
  0         0  
  79         116  
  79         71  
  79         179  
  79         198  
  79         239  
  61         122  
  61         141  
  61         64  
  61         112  
  61         103  
  122         99  
  122         194  
  122         311  
  61         94  
  61         128  
  61         187  
  79         149  
  76         117  
  76         219  
  3         4  
  3         6  
  0         0  
  0         0  
  3         7  
  3         8  
  6         333  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
45 8         29 $dispatch_installed{$pkg}{$name}= 1;
46             }
47             #eval(make_dispatch($pkg,$name)) || croak "internal error: $@"
48             # unless eval "defined \&${pkg}::$name";
49             }
50              
51             # REGISTER RESOLUTION FUNCTIONS FOR AMBIGUOUS AND NO-MATCH CALLS
52              
53             sub resolve_ambiguous
54             {
55 0     0 0 0 my $name = shift;
56 0 0 0     0 if (@_ == 1 && ref($_[0]) eq 'CODE')
57 0         0 { $ambiguous_handler{$name} = $_[0] }
58             else
59 0         0 { $ambiguous_handler{$name} = join ',', @_ }
60             }
61              
62             sub resolve_no_match
63             {
64 0     0 0 0 my $name = shift;
65 0 0 0     0 if (@_ == 1 && ref($_[0]) eq 'CODE')
66 0         0 { $no_match_handler{$name} = $_[0] }
67             else
68 0         0 { $no_match_handler{$name} = join ',', @_ }
69             }
70              
71             # GENERATE A SPECIAL PROXY OBJECT TO INDICATE THAT THE ANCESTOR OF AN OBJECT'S
72             # CLASS IS REQUIRED
73              
74             sub superclass
75             {
76 0     0 0 0 my ($obj, $super) = @_;
77 0 0 0     0 $super = ref($obj) || ( (~$obj&$obj) eq 0 ? '#' : '$' ) if @_ <= 1;
78 0 0       0 bless \$obj, (@_ > 1 )
79             ? "Class::Multimethods::SUPERCLASS_IS::$super"
80             : "Class::Multimethods::SUPERCLASS_OF::$super";
81             }
82              
83             sub _prettify
84             {
85 198 50   198   4076 $_[0] =~ s/Class::Multimethods::SUPERCLASS_IS:://
86             or $_[0] =~ s/Class::Multimethods::SUPERCLASS_OF::(.*)/superclass($1)/;
87             }
88              
89             # SQUIRREL AWAY THE PROFFERED SUB REF INDEXED BY THE MULTIMETHOD NAME
90             # AND THE TYPE NAMES SUPPLIED. CAN ALSO BE USED WITH JUST THE MULTIMETHOD
91             # NAME IN ORDER TO INSTALL A SUITABLE DISPATCH SUB INTO THE CALLING PACKAGE
92              
93             sub multimethod
94             {
95 27     27 1 658 my $package = (caller)[0];
96 27         42 my $name = shift;
97 27         41 install_dispatch($package,$name);
98              
99 27 100       56 if (@_) # NOT JUST INSTALLING A DISPATCH SUB...
100             {
101 26         27 my $code = pop;
102 26 50       55 croak "multimethod: last arg must be a code reference"
103             unless ref($code) eq 'CODE';
104              
105 26         44 my @types = @_;
106              
107 26         39 for ($Class::Multimethods::max_args{$name})
108 26 100 100     109 { $_ = @types if !defined || @types > $_ }
109 26         33 for ($Class::Multimethods::min_args{$name})
110 26 100 66     106 { $_ = @types if !defined || @types < $_ }
111            
112 26         41 my $sig = join ',', @types;
113              
114 26   100     91 $Class::Multimethods::hasgeneric{$name} ||= $sig =~ /\*/;
115              
116             carp "Multimethod $name($sig) redefined"
117 26 0 33     58 if $^W && exists $dispatch{$name}{$sig};
118 26         49 $dispatch{$name}{$sig} = $code;
119              
120             # NOTE: ADDING A MULTIMETHOD COMPROMISES CACHING
121             # THIS IS A DUMB, BUT FAST, FIX...
122 26         66 $cached{$name} = {};
123             }
124             }
125              
126              
127             # THIS IS THE ACTUAL MEAT OF THE PACKAGE -- A GENERIC DISPATCHING SUB
128             # WHICH EXPLORES THE %dispatch AND %cache HASHES LOOKING FOR A UNIQUE
129             # BEST MATCH...
130              
131             sub make_dispatch # ($name)
132             {
133 8     8 0 9 my ($pkg,$name) = @_;
134 8         13 my $code = q{
135              
136             sub PACKAGE::NAME
137             {
138             # MAP THE ARGS TO TYPE NAMES, MAP VALUES TO '#' (FOR NUMBERS)
139             # OR '$' (OTHERWISE). THEN BUILD A FUNCTION TYPE SIGNATURE
140             # (LIKE A "PATH" INTO THE VARIOUS TABLES)
141              
142             my $sig = "";
143             my $nexttype;
144             foreach ( @_ )
145             {
146             $nexttype = ref || ( (~$_&$_) eq 0 ? '#' : '$' );
147             $sig .= $nexttype;
148             $sig .= ",";
149             }
150             chop $sig;
151              
152             my $code = $Class::Multimethods::dispatch{'NAME'}{$sig}
153             || $Class::Multimethods::cached{'NAME'}{$sig};
154            
155             return $code->(@_) if ($code);
156              
157             my @types = split /,/, $sig;
158             for (my $i=1; $i<@types; $i++)
159             {
160             $_[$i] = ${$_[$i]}
161             if index($types[$i],'Class::Multimethods::SUPERCLASS')==0;
162             }
163             my %tried = (); # USED TO AVOID MULTIPLE MATCHES ON SAME SIG
164             my @code; # STORES LIST OF EQUALLY-CLOSE MATCHING SUBS
165             my @candidates = ( [@types] ); # STORES POSSIBLE MATCHING SIGS
166              
167             # TRY AND RESOLVE TO AN TYPE-EXPLICIT SIGNATURE (USING INHERITANCE)
168              
169             1 until (Class::Multimethods::resolve('NAME',\@candidates,\@code,\%tried) || !@candidates);
170              
171             # IF THAT DOESN'T WORK, TRY A GENERIC SIGNATURE (IF THERE ARE ANY)
172             # THE NESTED LOOPS GENERATE ALL POSSIBLE PERMUTATIONS OF GENERIC
173             # SIGNATURES IN SUCH A WAY THAT, EACH TIME resolve IS CALLED, ALL
174             # THE CANDIDATES ARE EQUALLY GENERIC (HAVE AN EQUAL NUMBER OF GENERIC
175             # PLACEHOLDERS)
176              
177             if ( @code == 0 && $Class::Multimethods::hasgeneric{'NAME'} )
178             {
179             # TRY GENERIC VERSIONS
180             my @gencandidates = ([@types]);
181             GENERIC: for (0..$#types)
182             {
183             @candidates = ();
184             for (my $gci=0; $gci<@gencandidates; $gci++)
185             {
186             for (my $i=0; $i<@types; $i++)
187             {
188             push @candidates,
189             [@{$gencandidates[$gci]}];
190             $candidates[-1][$i] = "*";
191             }
192             }
193             @gencandidates = @candidates;
194             1 until (Class::Multimethods::resolve('NAME',\@candidates,\@code,\%tried) || !@candidates);
195             last GENERIC if @code;
196             }
197             }
198              
199             # RESOLUTION PROCESS COMPLETED...
200             # IF EXACTLY ONE BEST MATCH, CALL IT...
201              
202             if ( @code == 1 )
203             {
204             $Class::Multimethods::cached{'NAME'}{$sig} = $code[0];
205             return $code[0]->(@_);
206             }
207              
208             # TWO OR MORE EQUALLY LIKELY CANDIDATES IS AMBIGUOUS...
209             elsif ( @code > 1)
210             {
211             my $handler = $Class::Multimethods::ambiguous_handler{'NAME'};
212             if (defined $handler)
213             {
214             return $handler->(@_)
215             if ref $handler;
216             return $Class::Multimethods::dispatch{'NAME'}{$handler}->(@_)
217             if defined $Class::Multimethods::dispatch{'NAME'}{$handler};
218             }
219             _prettify($sig);
220             croak "Cannot resolve call to multimethod NAME($sig). " .
221             "The multimethods:\n" .
222             join("\n",
223             map { "\tNAME(" . join(',',@$_) . ")" }
224             @candidates) .
225             "\nare equally viable";
226             }
227              
228             # IF *NO* CANDIDATE, NO WAY TO DISPATCH THE CALL
229             else
230             {
231             my $handler = $Class::Multimethods::no_match_handler{'NAME'};
232             if (defined $handler)
233             {
234             return $handler->(@_)
235             if ref $handler;
236             return $Class::Multimethods::dispatch{'NAME'}{$handler}->(@_)
237             if defined $Class::Multimethods::dispatch{'NAME'}{$handler};
238             }
239             _prettify($sig);
240             croak "No viable candidate for call to multimethod NAME($sig)";
241             }
242             }
243             1;
244              
245             };
246 8         75 $code =~ s/PACKAGE/$pkg/g;
247 8         137 $code =~ s/NAME/$name/g;
248 8         4879 return $code;
249             }
250              
251              
252             # THIS SUB TAKES A LIST OF EQUALLY LIKELY CANDIDATES (I.E. THE SAME NUMBER OF
253             # INHERITANCE STEPS AWAY FROM THE ACTUAL ARG TYPES) AND BUILDS A LIST OF
254             # MATCHING ONES. IF THERE AREN'T ANY MATCHES, IT BUILDS A NEW LIST OF
255             # CANDIDATES, BY GENERATING PERMUTATIONS OF THE SET OF PARENT TYPES FOR
256             # EACH ARG TYPE.
257              
258             sub resolve
259             {
260 1130     1130 0 1320 my ($name, $candidates, $matches, $tried) = @_;
261 1130         1153 my %newcandidates = ();
262 1130         1334 foreach my $candidate ( @$candidates )
263             {
264             # print "trying @$candidate...\n";
265              
266             # BUILD THE TYPE SIGNATURE AND ENSURE IT HASN'T ALREADY BEEN CHECKED
267              
268 1683         2287 my $sig = join ',', @$candidate;
269 1683 50       2726 next if $tried->{$sig};
270 1683         2035 $tried->{$sig} = 1;
271            
272             # LOOK FOR A MATCHING SUB REF IN THE DISPATCH TABLE AND REMEMBER IT...
273              
274 1683         1793 my $match = $Class::Multimethods::dispatch{$name}{$sig};
275 1683 100 66     2934 if ($match && ref($match) eq 'CODE')
276             {
277 135         143 push @$matches, $match;
278 135         186 next;
279             }
280              
281             # OTHERWISE, GENERATE A NEW SET OF CANDIDATES BY REPLACING EACH
282             # ARGUMENT TYPE IN TURN BY EACH OF ITS IMMEDIATE PARENTS. EACH SUCH
283             # NEW CANDIDATE MUST BE EXACTLY 1 DERIVATION MORE EXPENSIVE THAN
284             # THE CURRENT GENERATION OF CANDIDATES. NOTE, THAT IF A MATCH HAS
285             # BEEN FOUND AT THE CURRENT GENERATION, THERE IS NO NEED TO LOOK
286             # ANY DEEPER...
287              
288 1548 100       2279 if (!@$matches)
289             {
290 1520         2507 for (my $i = 0; $i<@$candidate ; $i++)
291             {
292 3049 100       5865 next if $candidate->[$i] =~ /[^\w:#]/;
293 1     1   5 no strict 'refs';
  1         1  
  1         238  
294 2897         1981 my @parents;
295 2897 100       5986 if ($candidate->[$i] eq '#')
    50          
    50          
296 2         4 { @parents = ('$') }
297             elsif ($candidate->[$i] =~ /\AClass::Multimethods::SUPERCLASS_IS::(.+)/)
298 0         0 { @parents = ($1) }
299             elsif ($candidate->[$i] =~ /\AClass::Multimethods::SUPERCLASS_OF::(.+)/)
300 0 0       0 { @parents = ($1 eq '#') ? '$' : @{$1."::ISA"} }
  0         0  
301             else
302 2895         2091 { @parents = @{$candidate->[$i]."::ISA"} }
  2895         5948  
303 2897         4296 foreach my $parent ( @parents )
304             {
305 1707         2148 my @newcandidate = @$candidate;
306 1707         1535 $newcandidate[$i] = $parent;
307 1707         6689 $newcandidates{join ',', @newcandidate} = [@newcandidate];
308             }
309             }
310            
311             }
312             }
313              
314             # IF NO MATCHES AT THE CURRENT LEVEL, RESET THE CANDIDATES TO THOSE AT
315             # THE NEXT LEVEL...
316              
317 1130 100       2909 @$candidates = values %newcandidates unless @$matches;
318              
319 1130         28599 return scalar @$matches;
320             }
321              
322             # SUPPORT FOR analyse
323              
324             my %children;
325             my %parents;
326              
327             sub build_relationships
328             {
329 1     1   5 no strict "refs";
  1         1  
  1         108  
330 0     0 0   %children = ( '$' => [ '#' ] );
331 0           %parents = ( '#' => [ '$' ] );
332 0           my (@packages) = @_;
333 0           foreach my $package (@packages)
334             {
335 0           foreach my $parent ( @{$package."::ISA"} )
  0            
336             {
337 0           push @{$children{$parent}}, $package;
  0            
338 0           push @{$parents{$package}}, $parent;
  0            
339             }
340             }
341             }
342              
343              
344             sub list_packages
345             {
346 1     1   4 no strict "refs";
  1         2  
  1         455  
347 0   0 0 0   my $self = $_[0]||"main::";
348 0           my @children = ( $self );
349 0           foreach ( keys %{$self} )
  0            
350             {
351 0 0 0       next unless /::$/ && $_ ne $self;
352 0           push @children, list_packages("$self$_")
353             }
354 0 0         @children = map { s/^main::(.+)$/$1/; s/::$//; $_ } @children
  0            
  0            
  0            
355             unless $_[0];
356 0           return @children;
357             }
358              
359             sub list_ancestors
360             {
361 0     0 0   my ($class) = @_;
362 0           my @ancestors = ();
363 0           foreach my $parent ( @{$parents{$class}} )
  0            
364             {
365 0           push @ancestors, list_ancestors($parent), $parent;
366             }
367 0           return @ancestors;
368             }
369              
370             sub list_descendents
371             {
372 0     0 0   my ($class) = @_;
373 0           my @descendents = ();
374 0           foreach my $child ( @{$children{$class}} )
  0            
375             {
376 0           push @descendents, $child, list_descendents($child);
377             }
378 0           return @descendents;
379             }
380              
381             sub list_hierarchy
382             {
383 0     0 0   my ($class) = @_;
384 0           my @hierarchy = list_ancestors($class);
385 0           push @hierarchy, $class;
386 0           push @hierarchy, list_descendents($class);
387 0           return @hierarchy;
388             }
389              
390             @Class::Multimethods::dont_analyse = qw
391             (
392             Exporter
393             DynaLoader
394             AutoLoader
395             );
396              
397             sub generate_argsets
398             {
399 0     0 0   my ($multimethod) = @_;
400              
401 0           my %ignore;
402 0           @ignore{@Class::Multimethods::dont_analyse} = ();
403              
404              
405 0 0         return unless $min_args{$multimethod};
406              
407 0           my @paramlists = ();
408              
409 0           foreach my $typeset ( keys %{$Class::Multimethods::dispatch{$multimethod}} )
  0            
410             {
411 0 0         next if $typeset =~ /\Q*/;
412 0           my @nexttypes = split /,/, $typeset;
413 0           for my $i (0..$#nexttypes)
414             {
415 0           for my $ancestor ( list_hierarchy $nexttypes[$i] )
416             {
417             $paramlists[$i]{$ancestor} = 1
418 0 0         unless exists $ignore{$ancestor};
419             }
420             }
421             }
422              
423 0           my @argsets = ();
424              
425 0           foreach (@paramlists) { $_ = [keys %{$_}] }
  0            
  0            
426              
427 1     1   676 use Data::Dumper;
  1         7795  
  1         1523  
428             # print Data::Dumper->Dump([@paramlists]);
429              
430 0           foreach my $argcount ($min_args{$multimethod}..$max_args{$multimethod})
431             {
432 0           push @argsets, combinations(@paramlists[0..$argcount-1]);
433             }
434              
435             # print STDERR Data::Dumper->Dump([@argsets]);
436              
437 0           return @argsets;
438             }
439              
440             sub combinations
441             {
442 0     0 0   my (@paramlists) = @_;
443 0 0         return map { [$_] } @{$paramlists[0]} if (@paramlists==1);
  0            
  0            
444 0           my @combs = ();
445 0           my @subcombs = combinations(@paramlists[1..$#paramlists]);
446 0           foreach my $firstparam (@{$paramlists[0]})
  0            
447             {
448 0           foreach my $subcomb ( @subcombs )
449             {
450 0           push @combs, [$firstparam, @{$subcomb}];
  0            
451             }
452             }
453 0           return @combs;
454             }
455              
456             sub analyse
457             {
458 0     0 0   my ($multimethod, @argsets) = @_;
459 0           my ($package,$file,$line) = caller(0);
460 0   0       my ($sub) = (caller(1))[3] || "main code";
461 0           my $case_count = @argsets;
462 0           my $ambiguous_handler = $ambiguous_handler{$multimethod};
463 0           my $no_match_handler = $no_match_handler{$multimethod};
464 0 0 0       $ambiguous_handler = "$multimethod($ambiguous_handler)"
465             if $ambiguous_handler && ref($ambiguous_handler) ne "CODE";
466 0 0 0       $no_match_handler = "$multimethod($no_match_handler)"
467             if $no_match_handler && ref($no_match_handler) ne "CODE";
468 0           build_relationships list_packages;
469 0 0         if ($case_count)
470             {
471 0           my @newargsets;
472 0           foreach my $argset ( @argsets )
473             {
474 0 0         my @argset = map { ref eq 'ARRAY' ? $_ : [$_] } @$argset;
  0            
475 0           push @newargsets, combinations(@argset);
476             }
477 0           @argsets = @newargsets;
478 0           $case_count = @argsets;
479             }
480             else
481             {
482 0           @argsets = generate_argsets($multimethod);
483 0           $case_count = @argsets;
484 0 0         unless ($case_count)
485             {
486 0           print STDERR "[No variants found for $multimethod. No analysis possible.]\n\n";
487 0           print STDERR "="x72, "\n\n";
488 0           return;
489              
490             }
491 0           print STDERR "[Generated $case_count test cases for $multimethod]\n\n"
492             }
493              
494 0           print STDERR "Analysing calls to $multimethod from $sub ($file, line $line):\n";
495 0           my $case = 1;
496              
497 0           my $successes = 0;
498 0           my @fails = ();
499 0           my @ambigs = ();
500              
501 0           foreach my $argset ( @argsets )
502             {
503 0           my $callsig = "${multimethod}(".join(",",@$argset).")";
504 0           print STDERR "\n\t[$case/$case_count] For call to $callsig:\n\n";
505 0           $case++;
506             my @ordered = sort {
507 0           $a->{wrong_length} - $b->{wrong_length}
508             ||
509 0           @{$a->{incomp}} - @{$b->{incomp}}
  0            
510             ||
511             $a->{generic} - $b->{generic}
512             ||
513             $a->{sum_dist} <=> $b->{sum_dist}
514 0 0 0       }
      0        
515             evaluate($multimethod, $argset);
516              
517              
518 0 0 0       if ($ordered[0] && !@{$ordered[0]->{incomp}})
  0            
519             {
520 0           my $i;
521 0           for ($i=1; $i<@ordered; $i++)
522             {
523 0           last if @{$ordered[$i]->{incomp}} ||
524             $ordered[$i]->{wrong_length} ||
525             $ordered[$i]->{sum_dist} >
526             $ordered[0]->{sum_dist} ||
527             $ordered[$i]->{generic} !=
528 0 0 0       $ordered[0]->{generic};
      0        
      0        
529             }
530 0           $ordered[$_]->{less_viable} = 1 for ($i..$#ordered);
531 0 0         if ($i>1)
532             {
533 0           $ordered[$i]->{ambig} = 1 while ($i-->0)
534             }
535             }
536              
537 0           my $first = 1;
538 0           my $min_dist = 0;
539 0           push @fails, "\t\t$callsig\n"; # ASSUME THE WORST
540              
541             # CHECK FOR REOLUTION IF DISPATCH FAILS
542              
543 0           my $winner = $ordered[0];
544 0 0 0       if ($winner && $winner->{ambig} && $ambiguous_handler)
    0 0        
      0        
      0        
      0        
545             {
546 0           print STDERR "\t\t(+) $ambiguous_handler\n\t\t\t>>> Ambiguous dispatch handler invoked.\n\n";
547 0           $first = 0;
548 0           $successes++;
549 0           pop @fails;
550             }
551             elsif ($winner
552             && (@{$winner->{incomp}} || $winner->{wrong_length})
553             && $no_match_handler )
554             {
555 0           print STDERR "\t\t(+) $no_match_handler\n\t\t\t>>> Dispatch failure handler invoked.\n\n";
556 0           $first = 0;
557 0           $successes++;
558 0           pop @fails;
559             }
560 0           foreach my $variant (@ordered)
561             {
562 0 0 0       if ($variant->{ambig})
    0          
    0          
    0          
    0          
    0          
    0          
563             {
564 0           print STDERR "\t\t(?) $variant->{sig}\n\t\t\t>>> Ambiguous. Distance: $variant->{sum_dist}\n";
565 0 0         push @ambigs, pop @fails if $first;
566             }
567 0           elsif (@{$variant->{incomp}} == 1)
568             {
569 0           print STDERR "\t\t(-) $variant->{sig}\n\t\t\t>>> Not viable. Incompatible argument: ", @{$variant->{incomp}}, "\n";
  0            
570             }
571 0           elsif (@{$variant->{incomp}})
572             {
573 0           print STDERR "\t\t(-) $variant->{sig}\n\t\t\t>>> Not viable. Incompatible arguments: ", join(",",@{$variant->{incomp}}), "\n";
  0            
574             }
575             elsif ($variant->{wrong_length})
576             {
577 0           print STDERR "\t\t(-) $variant->{sig}\n\t\t\t>>> Not viable. Wrong number of arguments\n";
578             }
579             elsif ($first)
580             {
581 0           print STDERR "\t\t(+) $variant->{sig}\n\t\t\t>>> Target. Distance: $variant->{sum_dist}\n\n";
582 0           $min_dist = $variant->{sum_dist};
583 0           $successes++;
584 0           pop @fails;
585             }
586             elsif ($variant->{generic} && $variant->{sum_dist} < $min_dist)
587             {
588 0           print STDERR "\t\t(*) $variant->{sig}\n\t\t\t>>> Viable, but generic. Distance: $variant->{sum_dist} (generic)\n";
589             }
590             elsif ($variant->{generic})
591             {
592 0           print STDERR "\t\t(*) $variant->{sig}\n\t\t\t>>> Viable. Distance: $variant->{sum_dist} (generic)\n";
593             }
594             else
595             {
596 0           print STDERR "\t\t(x) $variant->{sig}\n\t\t\t>>> Viable. Distance: $variant->{sum_dist}\n";
597             }
598 0           $first = 0;
599             }
600 0           print STDERR "\n";
601             }
602 0           print STDERR "\n", "-"x72, "\nSummary for calls to $multimethod from $sub ($file, line $line):\n\n";
603              
604 0           printf STDERR "\tSuccessful dispatch in %2.0f%% of calls\n",
605             $successes/$case_count*100;
606 0           printf STDERR "\tDispatch ambiguous for %2.0f%% of calls\n",
607             @ambigs/$case_count*100;
608 0           printf STDERR "\tWas unable to dispatch %2.0f%% of calls\n",
609             @fails/$case_count*100;
610              
611 0 0         print STDERR "\nAmbiguous calls:\n", @ambigs if @ambigs;
612 0 0         print STDERR "\nUndispatchable:\n", @fails if @fails;
613              
614 0           print STDERR "\n", "="x72, "\n\n";
615              
616             }
617              
618             my %distance;
619             sub distance
620             {
621 0     0 0   my ($from, $to) = @_;
622              
623 0 0         return 0 if $from eq $to;
624 0 0         return -1 if $to eq '*';
625 0 0         return $distance{$from}{$to} if defined $distance{$from}{$to};
626              
627 0 0         if ($parents{$from})
628             {
629 0           foreach my $parent ( @{$parents{$from}} )
  0            
630             {
631 0           my $distance = distance($parent,$to);
632 0 0         if (defined $distance)
633             {
634 0           $distance{$from}{$to} = $distance+1;
635 0           return $distance+1;
636             }
637             }
638             }
639 0           return undef;
640             }
641              
642             sub evaluate
643             {
644 0     0 0   my ($name, $types) = @_;
645 0           my @results = ();
646 0           my $sig = join ',', @$types;
647              
648 0           SET: foreach my $typeset ( keys %{$Class::Multimethods::dispatch{$name}} )
  0            
649             {
650            
651 0           push @results, { sig => "$name($typeset)",
652             incomp => [],
653             sum_dist => 0,
654             wrong_length => 0,
655             generic => 0,
656             };
657 0           my @nexttypes = split /,/, $typeset;
658 0 0         if (@nexttypes != @$types)
659             {
660 0           $results[-1]->{wrong_length} = 1;
661 0           next SET;
662             }
663              
664 0           my @dist;
665 0           PARAM: for (my $i=0; $i<@$types; $i++)
666             {
667 0           my $nextdist = distance($types->[$i], $nexttypes[$i]);
668 0           push @{$results[-1]->{dist}}, $nextdist;
  0            
669 0 0         if (!defined $nextdist)
    0          
670             {
671 0           push @{$results[-1]->{incomp}}, $i;
  0            
672             }
673             elsif ($nextdist < 0)
674             {
675 0           $results[-1]->{generic} = 1;
676             }
677             else
678             {
679 0           $results[-1]->{sum_dist} += $nextdist
680             }
681             }
682             }
683 0           return @results;
684             }
685              
686              
687             1;
688             __END__