File Coverage

blib/lib/Trace/Mask/Util.pm
Criterion Covered Total %
statement 96 96 100.0
branch 40 42 95.2
condition 34 40 85.0
subroutine 18 18 100.0
pod 7 7 100.0
total 195 203 96.0


line stmt bran cond sub pod time code
1             package Trace::Mask::Util;
2 6     6   196026 use strict;
  6         13  
  6         141  
3 6     6   29 use warnings;
  6         9  
  6         148  
4              
5 6     6   30 use Carp qw/croak carp/;
  6         9  
  6         299  
6              
7 6     6   35 use Scalar::Util qw/reftype looks_like_number/;
  6         10  
  6         278  
8 6     6   30 use B;
  6         9  
  6         369  
9              
10             our $VERSION = "0.000001";
11              
12 6     6   35 use base 'Exporter';
  6         7  
  6         872  
13             our @EXPORT_OK = qw{
14             update_mask
15             validate_mask
16             get_mask
17             mask_line
18             mask_call
19             mask_sub
20             mask_frame
21             };
22              
23             my %VALID_MASK = (
24             hide => 1,
25             no_start => 1,
26             shift => 1,
27             stop => 1,
28             restart => 1,
29             lock => 1,
30             );
31              
32 6     6   34 sub _MASKS() { no warnings 'once'; \%Trace::Mask::MASKS }
  6     15764   11  
  6         7852  
  15764         23983  
33              
34             sub _subname {
35 95     95   419 my $cobj = B::svref_2object($_[0]);
36 95         795 my $package = $cobj->GV->STASH->NAME;
37 95         672 my $subname = $cobj->GV->NAME;
38 95         326 return "$package\::$subname";
39             }
40              
41             sub _validate_mask {
42 1152     1152   1593 my $mask = shift;
43 1152 100       2070 my @errors = validate_mask($mask) or return;
44 4         26 my @caller = caller(1);
45 4         11 my $out = join "\n" => map {" $_"} @errors;
  4         20  
46 4         36 die "Invalid mask at $caller[1] line $caller[2].\n$out\n"
47             }
48              
49             sub _update_mask {
50 1138     1138   2009 my ($file, $line, $sub, $mask) = @_;
51              
52 1138 100       2234 my $name = ref $sub ? _subname($sub) : $sub;
53              
54 1138         1963 my $masks = _MASKS();
55              
56             # Get existing ref, if any
57 1138         3401 my $ref = $masks->{$file}->{$line}->{$name};
58              
59             # No ref, easy!
60 1138 100       3252 return $masks->{$file}->{$line}->{$name} = {%$mask}
61             unless $ref;
62              
63             # Merge new mask into old
64 917         4047 %$ref = (%$ref, %$mask);
65 917         2116 return;
66             }
67              
68             sub update_mask {
69 8     8 1 14656 my ($file, $line, $sub, $mask) = @_;
70 8         20 _validate_mask($mask);
71 7         18 _update_mask(@_);
72             }
73              
74             sub validate_mask {
75 1157     1157 1 20598 my ($mask) = @_;
76              
77 1157 100 100     8723 return ("Mask must be a hashref")
      100        
78             unless $mask && ref($mask) && reftype($mask) eq 'HASH';
79              
80 1153         1335 my @errors;
81              
82             # Sort the keys to keep it consistent
83 1153         3508 for my $key (sort keys %$mask) {
84 1977 100       5428 next if $key =~ m/^\d+$/; # integer keys are always valid
85 1429 100       3812 next if $VALID_MASK{$key};
86 7         20 push @errors => "invalid mask option '$key'";
87             }
88              
89 1153 100       2960 if (my $shift = $mask->{shift}) {
90 135 100 66     859 push @errors => "'shift' value must be a positive integer"
91             unless $shift =~ m/^\d+$/ && $shift >= 0;
92             }
93              
94 1153 100       2719 if (my $hide = $mask->{hide}) {
95 520 50 33     3222 push @errors => "'hide' value must be a positive integer"
96             unless $hide =~ m/^\d+$/ && $hide >= 0;
97             }
98              
99 1153         4101 return @errors;
100             }
101              
102             sub mask_line {
103 16     16 1 5502 my ($mask, $delta, @subs) = @_;
104 16         99 my ($pkg, $file, $line) = caller(0);
105              
106 16         49 _validate_mask($mask);
107              
108 15 100 100     424 croak "The second argument to mask_line() must be an integer"
      66        
109             if $delta && (ref($delta) || $delta !~ m/^-?\d+$/);
110              
111 13 100       49 push @subs => '*' unless @subs;
112 13 100       39 $line += $delta if $delta;
113              
114 13         41 _update_mask($file, $line, $_, $mask) for @subs;
115 13         7173 return;
116             }
117              
118             sub mask_call {
119 94     94 1 10324 my $mask = shift;
120 94         130 my $sub = shift;
121 94         474 my ($pkg, $file, $line) = caller(0);
122              
123 94         235 _validate_mask($mask);
124              
125 93 100 100     473 $sub = $pkg->can($sub) if $sub && !ref($sub);
126              
127 93 100 66     1167 croak "The second argument to mask_call() must be a coderef, or the name of a sub to call"
      100        
128             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
129              
130 89         169 _update_mask($file, $line, $sub, $mask);
131              
132 89         167 @_ = (@_); # Hide the shifted args
133 89         1571 goto &$sub;
134             }
135              
136             sub mask_sub {
137 9     9 1 8319 my ($mask, $sub, $file, $line) = @_;
138 9   100     35 $file ||= '*';
139 9   100     29 $line ||= '*';
140              
141 9         17 _validate_mask($mask);
142              
143 8 100 100     51 $sub = caller->can($sub) if $sub && !ref($sub);
144              
145 8 100 66     491 croak "The second argument to mask_sub() must be a coderef, or the name of a sub in the calling package"
      100        
146             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
147              
148 4         9 my $name = _subname($sub);
149 4 100       116 croak "mask_sub() cannot be used on an unamed sub"
150             if $name =~ m/__ANON__$/;
151              
152 3         6 _update_mask($file, $line, $name, $mask);
153 3         8 return;
154             }
155              
156             sub mask_frame {
157 1025     1025 1 174454 my %mask = @_;
158              
159 1025         1970 _validate_mask(\%mask);
160              
161 1025         5955 my ($pkg, $file, $line, $name) = caller(1);
162 1025         2572 _update_mask($file, $line, $name, \%mask);
163              
164 1025         2786 return;
165             }
166              
167             sub get_mask {
168 14626     14626 1 28965 my ($file, $line, $sub) = @_;
169              
170 14626 50       25446 my $name = ref($sub) ? _subname($sub) : $sub;
171              
172 14626         22906 my $masks = _MASKS();
173              
174 14626 100       55659 return {lock => $1} if $sub =~ m/(?:^|:)(END|BEGIN|UNITCHECK|CHECK|INIT|DESTROY|import|unimport)$/;
175              
176 72965         131742 my @order = grep { defined $_ } (
177             $masks->{$file}->{'*'}->{'*'},
178             $masks->{$file}->{$line}->{'*'},
179             $masks->{'*'}->{'*'}->{$name},
180             $masks->{$file}->{'*'}->{$name},
181 14593         78940 $masks->{$file}->{$line}->{$name},
182             );
183              
184 14593 100       49159 return {} unless @order;
185 3317         5035 return { map { %{$_} } @order };
  3321         3332  
  3321         17117  
186             }
187              
188             1;
189              
190             __END__