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   82640 use strict;
  6         8  
  6         130  
3 6     6   18 use warnings;
  6         6  
  6         118  
4              
5 6     6   14 use Carp qw/croak carp/;
  6         5  
  6         231  
6              
7 6     6   19 use Scalar::Util qw/reftype looks_like_number/;
  6         6  
  6         198  
8 6     6   18 use B;
  6         6  
  6         252  
9              
10             our $VERSION = "0.000008";
11              
12 6     6   19 use base 'Exporter';
  6         7  
  6         564  
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             pause => 1,
29             restart => 1,
30             lock => 1,
31             );
32              
33 6     6   23 sub _MASKS() { no warnings 'once'; \%Trace::Mask::MASKS }
  6     7531   6  
  6         5349  
  7531         6513  
34              
35             sub _subname {
36 95     95   331 my $cobj = B::svref_2object($_[0]);
37 95         500 my $package = $cobj->GV->STASH->NAME;
38 95         296 my $subname = $cobj->GV->NAME;
39 95         219 return "$package\::$subname";
40             }
41              
42             sub _validate_mask {
43 1152     1152   869 my $mask = shift;
44 1152 100       1167 my @errors = validate_mask($mask) or return;
45 4         17 my @caller = caller(1);
46 4         6 my $out = join "\n" => map {" $_"} @errors;
  4         11  
47 4         28 die "Invalid mask at $caller[1] line $caller[2].\n$out\n"
48             }
49              
50             sub _update_mask {
51 1138     1138   945 my ($file, $line, $sub, $mask) = @_;
52              
53 1138 100       1453 my $name = ref $sub ? _subname($sub) : $sub;
54              
55 1138         1094 my $masks = _MASKS();
56              
57             # Get existing ref, if any
58 1138         1923 my $ref = $masks->{$file}->{$line}->{$name};
59              
60             # No ref, easy!
61 1138 100       2092 return $masks->{$file}->{$line}->{$name} = {%$mask}
62             unless $ref;
63              
64             # Merge new mask into old
65 917         2561 %$ref = (%$ref, %$mask);
66 917         1151 return;
67             }
68              
69             sub update_mask {
70 8     8 1 10989 my ($file, $line, $sub, $mask) = @_;
71 8         14 _validate_mask($mask);
72 7         20 _update_mask(@_);
73             }
74              
75             sub validate_mask {
76 1157     1157 1 3040 my ($mask) = @_;
77              
78 1157 100 100     5951 return ("Mask must be a hashref")
      100        
79             unless $mask && ref($mask) && reftype($mask) eq 'HASH';
80              
81 1153         798 my @errors;
82              
83             # Sort the keys to keep it consistent
84 1153         2316 for my $key (sort keys %$mask) {
85 1977 100       3543 next if $key =~ m/^\d+$/; # integer keys are always valid
86 1429 100       2386 next if $VALID_MASK{$key};
87 7         20 push @errors => "invalid mask option '$key'";
88             }
89              
90 1153 100       1791 if (my $shift = $mask->{shift}) {
91 135 100 66     619 push @errors => "'shift' value must be a positive integer"
92             unless $shift =~ m/^\d+$/ && $shift >= 0;
93             }
94              
95 1153 100       1587 if (my $hide = $mask->{hide}) {
96 520 50 33     2364 push @errors => "'hide' value must be a positive integer"
97             unless $hide =~ m/^\d+$/ && $hide >= 0;
98             }
99              
100 1153         2458 return @errors;
101             }
102              
103             sub mask_line {
104 16     16 1 3156 my ($mask, $delta, @subs) = @_;
105 16         74 my ($pkg, $file, $line) = caller(0);
106              
107 16         37 _validate_mask($mask);
108              
109 15 100 100     286 croak "The second argument to mask_line() must be an integer"
      66        
110             if $delta && (ref($delta) || $delta !~ m/^-?\d+$/);
111              
112 13 100       28 push @subs => '*' unless @subs;
113 13 100       23 $line += $delta if $delta;
114              
115 13         39 _update_mask($file, $line, $_, $mask) for @subs;
116 13         4612 return;
117             }
118              
119             sub mask_call {
120 94     94 1 6349 my $mask = shift;
121 94         82 my $sub = shift;
122 94         292 my ($pkg, $file, $line) = caller(0);
123              
124 94         149 _validate_mask($mask);
125              
126 93 100 100     322 $sub = $pkg->can($sub) if $sub && !ref($sub);
127              
128 93 100 66     892 croak "The second argument to mask_call() must be a coderef, or the name of a sub to call"
      100        
129             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
130              
131 89         124 _update_mask($file, $line, $sub, $mask);
132              
133 89         107 @_ = (@_); # Hide the shifted args
134 89         1081 goto &$sub;
135             }
136              
137             sub mask_sub {
138 9     9 1 5372 my ($mask, $sub, $file, $line) = @_;
139 9   100     29 $file ||= '*';
140 9   100     19 $line ||= '*';
141              
142 9         10 _validate_mask($mask);
143              
144 8 100 100     44 $sub = caller->can($sub) if $sub && !ref($sub);
145              
146 8 100 66     341 croak "The second argument to mask_sub() must be a coderef, or the name of a sub in the calling package"
      100        
147             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
148              
149 4         4 my $name = _subname($sub);
150 4 100       81 croak "mask_sub() cannot be used on an unamed sub"
151             if $name =~ m/__ANON__$/;
152              
153 3         4 _update_mask($file, $line, $name, $mask);
154 3         3 return;
155             }
156              
157             sub mask_frame {
158 1025     1025 1 105374 my %mask = @_;
159              
160 1025         1158 _validate_mask(\%mask);
161              
162 1025         4048 my ($pkg, $file, $line, $name) = caller(1);
163 1025         1609 _update_mask($file, $line, $name, \%mask);
164              
165 1025         1396 return;
166             }
167              
168             sub get_mask {
169 6393     6393 1 7932 my ($file, $line, $sub) = @_;
170              
171 6393 50       6452 my $name = ref($sub) ? _subname($sub) : $sub;
172              
173 6393         5705 my $masks = _MASKS();
174              
175 6393 100       16844 return {lock => $1} if $sub =~ m/(?:^|:)(END|BEGIN|UNITCHECK|CHECK|INIT|DESTROY|import|unimport)$/;
176              
177 31800         29742 my @order = grep { defined $_ } (
178             $masks->{$file}->{'*'}->{'*'},
179             $masks->{$file}->{$line}->{'*'},
180             $masks->{'*'}->{'*'}->{$name},
181             $masks->{$file}->{'*'}->{$name},
182 6360         17494 $masks->{$file}->{$line}->{$name},
183             );
184              
185 6360 100       12954 return {} unless @order;
186 1158         933 return { map { %{$_} } @order };
  1162         809  
  1162         3911  
187             }
188              
189             1;
190              
191             __END__