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   192030 use strict;
  6         11  
  6         156  
3 6     6   31 use warnings;
  6         13  
  6         161  
4              
5 6     6   34 use Carp qw/croak carp/;
  6         10  
  6         399  
6              
7 6     6   38 use Scalar::Util qw/reftype looks_like_number/;
  6         10  
  6         315  
8 6     6   31 use B;
  6         10  
  6         432  
9              
10             our $VERSION = "0.000001";
11              
12 6     6   49 use base 'Exporter';
  6         9  
  6         897  
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     16547   11  
  6         8715  
  16547         26321  
33              
34             sub _subname {
35 95     95   422 my $cobj = B::svref_2object($_[0]);
36 95         773 my $package = $cobj->GV->STASH->NAME;
37 95         513 my $subname = $cobj->GV->NAME;
38 95         325 return "$package\::$subname";
39             }
40              
41             sub _validate_mask {
42 1152     1152   1681 my $mask = shift;
43 1152 100       2375 my @errors = validate_mask($mask) or return;
44 4         27 my @caller = caller(1);
45 4         10 my $out = join "\n" => map {" $_"} @errors;
  4         15  
46 4         36 die "Invalid mask at $caller[1] line $caller[2].\n$out\n"
47             }
48              
49             sub _update_mask {
50 1138     1138   2261 my ($file, $line, $sub, $mask) = @_;
51              
52 1138 100       2412 my $name = ref $sub ? _subname($sub) : $sub;
53              
54 1138         2049 my $masks = _MASKS();
55              
56             # Get existing ref, if any
57 1138         3800 my $ref = $masks->{$file}->{$line}->{$name};
58              
59             # No ref, easy!
60 1138 100       3382 return $masks->{$file}->{$line}->{$name} = {%$mask}
61             unless $ref;
62              
63             # Merge new mask into old
64 917         4345 %$ref = (%$ref, %$mask);
65 917         2277 return;
66             }
67              
68             sub update_mask {
69 8     8 1 13661 my ($file, $line, $sub, $mask) = @_;
70 8         23 _validate_mask($mask);
71 7         24 _update_mask(@_);
72             }
73              
74             sub validate_mask {
75 1157     1157 1 20248 my ($mask) = @_;
76              
77 1157 100 100     9168 return ("Mask must be a hashref")
      100        
78             unless $mask && ref($mask) && reftype($mask) eq 'HASH';
79              
80 1153         1659 my @errors;
81              
82             # Sort the keys to keep it consistent
83 1153         3675 for my $key (sort keys %$mask) {
84 1977 100       5760 next if $key =~ m/^\d+$/; # integer keys are always valid
85 1429 100       4698 next if $VALID_MASK{$key};
86 7         19 push @errors => "invalid mask option '$key'";
87             }
88              
89 1153 100       3221 if (my $shift = $mask->{shift}) {
90 135 100 66     908 push @errors => "'shift' value must be a positive integer"
91             unless $shift =~ m/^\d+$/ && $shift >= 0;
92             }
93              
94 1153 100       2663 if (my $hide = $mask->{hide}) {
95 520 50 33     3664 push @errors => "'hide' value must be a positive integer"
96             unless $hide =~ m/^\d+$/ && $hide >= 0;
97             }
98              
99 1153         4318 return @errors;
100             }
101              
102             sub mask_line {
103 16     16 1 4826 my ($mask, $delta, @subs) = @_;
104 16         96 my ($pkg, $file, $line) = caller(0);
105              
106 16         46 _validate_mask($mask);
107              
108 15 100 100     450 croak "The second argument to mask_line() must be an integer"
      66        
109             if $delta && (ref($delta) || $delta !~ m/^-?\d+$/);
110              
111 13 100       57 push @subs => '*' unless @subs;
112 13 100       44 $line += $delta if $delta;
113              
114 13         41 _update_mask($file, $line, $_, $mask) for @subs;
115 13         6813 return;
116             }
117              
118             sub mask_call {
119 94     94 1 10082 my $mask = shift;
120 94         135 my $sub = shift;
121 94         490 my ($pkg, $file, $line) = caller(0);
122              
123 94         250 _validate_mask($mask);
124              
125 93 100 100     472 $sub = $pkg->can($sub) if $sub && !ref($sub);
126              
127 93 100 66     1183 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         188 _update_mask($file, $line, $sub, $mask);
131              
132 89         167 @_ = (@_); # Hide the shifted args
133 89         1773 goto &$sub;
134             }
135              
136             sub mask_sub {
137 9     9 1 6545 my ($mask, $sub, $file, $line) = @_;
138 9   100     36 $file ||= '*';
139 9   100     30 $line ||= '*';
140              
141 9         18 _validate_mask($mask);
142              
143 8 100 100     52 $sub = caller->can($sub) if $sub && !ref($sub);
144              
145 8 100 66     520 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         8 my $name = _subname($sub);
149 4 100       117 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 197002 my %mask = @_;
158              
159 1025         2227 _validate_mask(\%mask);
160              
161 1025         6206 my ($pkg, $file, $line, $name) = caller(1);
162 1025         2813 _update_mask($file, $line, $name, \%mask);
163              
164 1025         2834 return;
165             }
166              
167             sub get_mask {
168 15409     15409 1 28982 my ($file, $line, $sub) = @_;
169              
170 15409 50       27737 my $name = ref($sub) ? _subname($sub) : $sub;
171              
172 15409         26770 my $masks = _MASKS();
173              
174 15409 100       61599 return {lock => $1} if $sub =~ m/(?:^|:)(END|BEGIN|UNITCHECK|CHECK|INIT|DESTROY|import|unimport)$/;
175              
176 76880         146941 my @order = grep { defined $_ } (
177             $masks->{$file}->{'*'}->{'*'},
178             $masks->{$file}->{$line}->{'*'},
179             $masks->{'*'}->{'*'}->{$name},
180             $masks->{$file}->{'*'}->{$name},
181 15376         82439 $masks->{$file}->{$line}->{$name},
182             );
183              
184 15376 100       54081 return {} unless @order;
185 3578         6168 return { map { %{$_} } @order };
  3582         4195  
  3582         18264  
186             }
187              
188             1;
189              
190             __END__