File Coverage

blib/lib/Devel/MAT/ToolBase/GraphWalker.pm
Criterion Covered Total %
statement 44 57 77.1
branch 7 12 58.3
condition 10 18 55.5
subroutine 9 10 90.0
pod 0 2 0.0
total 70 99 70.7


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2020 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::ToolBase::GraphWalker 0.50;
7              
8 5     5   73 use v5.14;
  5         17  
9 5     5   30 use warnings;
  5         10  
  5         194  
10 5     5   33 use base qw( Devel::MAT::Tool );
  5         11  
  5         616  
11 5     5   2609 use utf8;
  5         63  
  5         32  
12              
13 5     5   194 use List::Util qw( any pairs );
  5         13  
  5         345  
14 5     5   45 use List::UtilsBy qw( nsort_by );
  5         11  
  5         2904  
15              
16             my %STRENGTH_ORDER = (
17             strong => 1,
18             weak => 2,
19             indirect => 3,
20             inferred => 4,
21             );
22              
23             my $next_id;
24             my %id_for;
25             my %seen;
26              
27             sub reset
28             {
29 0     0 0 0 $next_id = "A";
30 0         0 undef %id_for;
31 0         0 undef %seen;
32             }
33              
34             sub walk_graph
35             {
36 5     5 0 37 my $self = shift;
37 5         15 my ( $node, @args ) = @_;
38              
39 5         15 my $addr = $node->addr;
40 5         17 my @roots = $node->roots;
41 5         20 my @edges = $node->edges_in;
42              
43 5 50 66     21 if( !@roots and !@edges ) {
44 0         0 $self->on_walk_nothing( $node, @args );
45 0         0 return;
46             }
47              
48 5 50 66     75 if( @roots == 1 and $roots[0] eq "EDEPTH" ) {
49 0         0 $self->on_walk_EDEPTH( $node, @args );
50 0         0 return;
51             }
52              
53             # Don't bother showing any non-root edges if we have a strong root
54 5 100   3   71 @edges = () if any { $_->strength eq "strong" } @roots;
  3         14  
55              
56 5 50 66     51 if( @edges > 0 and $seen{$addr} ) {
57 0         0 my $cyclic = $seen{$addr} == 1;
58 0         0 my $id = $id_for{$addr};
59              
60 0         0 $self->on_walk_again( $node, $cyclic, $id, @args );
61 0         0 return;
62             }
63              
64 5         11 $seen{$addr}++;
65              
66 5         15 foreach my $idx ( 0 .. $#roots ) {
67 3         9 my $root = $roots[$idx];
68 3   33     22 my $isfinal = $idx == $#roots && !@edges;
69              
70 3         14 $self->on_walk_root( $node, $root, $isfinal, @args );
71             }
72              
73 5     4   115 my @refs = nsort_by { $STRENGTH_ORDER{$_->[0]->strength} } pairs @edges;
  4         44  
74 5         72 foreach my $idx ( 0 .. $#refs ) {
75 4         10 my ( $ref, $refnode ) = @{ $refs[$idx] };
  4         12  
76 4         11 my $is_final = $idx == $#refs;
77              
78 4         8 my $ref_id;
79 4 50 66     17 if( $refnode->edges_out > 1 and not $refnode->roots and not $id_for{$refnode->addr} ) {
      33        
80 0         0 $ref_id = $id_for{$refnode->addr} = $next_id++;
81             }
82              
83 4         16 my @subargs =
84             $self->on_walk_ref( $node, $ref, $refnode->sv, $ref_id, $is_final, @args );
85              
86 4 50       13 if( $refnode->addr == $addr ) {
87 0         0 $self->on_walk_itself( $node, @subargs );
88             }
89             else {
90 4         55 $self->walk_graph( $refnode, @subargs );
91             }
92             }
93              
94 5         42 $seen{$addr}++;
95             }
96              
97             0x55AA;