File Coverage

blib/lib/Devel/StealthDebug.pm
Criterion Covered Total %
statement 156 225 69.3
branch 39 56 69.6
condition 1 3 33.3
subroutine 20 34 58.8
pod 0 12 0.0
total 216 330 65.4


line stmt bran cond sub pod time code
1              
2             package Devel::StealthDebug;
3              
4             require Exporter;
5             @ISA=qw(Exporter);
6              
7             @EXPORT=(); # put the public function here
8             @EXPORT_OK=(); # to unable a non-stealth interface
9              
10              
11 14     14   672019 use strict;
  14         38  
  14         495  
12 14     14   74 use Carp;
  14         25  
  14         1155  
13 14     14   21010 use Filter::Simple;
  14         429192  
  14         110  
14              
15             our $SOURCE = 0;
16             our $VERSION = '1.008'; # Beware ! 1.1.2 sould be 1.001002
17             our $TABLEN = 2;
18             our $ENABLE = 1;
19             our $DUMPER = 0;
20              
21             our $Emit = 'carp';
22             our $Counter = 1;
23              
24             my %Wait_Cond;
25              
26             sub import {
27             shift;
28              
29             while (my $imported = shift) {
30             if ($imported eq 'SOURCE') {
31             my $file = shift;
32             open SOURCE,"> $file";
33             $SOURCE = 1;
34             } elsif ($imported eq 'emit_type') {
35             $Emit = shift;
36             if ($Emit =~ m:/:) {
37             my $tfh;
38             open($tfh,">>$Emit") or die $!; # replace filename by filehandle.
39             select($tfh);$|++;
40             $Emit=$tfh;
41             }
42             } elsif ($imported eq 'DUMPER') {
43             $DUMPER = shift ;
44             } elsif ($imported eq 'ENABLE') {
45             my $file_or_not = shift;
46              
47             $ENABLE = $file_or_not; # By default assume it's a value
48              
49             if (-e $file_or_not) { # If the file exists
50            
51             $ENABLE = 0; # disable ENABLE
52            
53             open INFILE,"<$file_or_not" or croak "Can't open $file_or_not ($!)";
54             while(my $authorized = ) {
55             chomp $authorized;
56             if ($0 =~ /$authorized/) { # unless the file allows it.
57             $ENABLE = 1; # unless
58             last;
59             }
60             }
61             close INFILE;
62             }
63             } else {
64             croak "Unknown $imported option ($imported @_)";
65             }
66             }
67             if ($DUMPER) {
68             require Data::Dumper ;
69             $Data::Dumper::Indent = 1;
70             $Data::Dumper::Sortkeys = 1;
71             }
72             }
73              
74             sub emit {
75 9 100   9 0 4318 if ($Emit eq 'carp') {
    100          
    100          
    50          
76 1         255 carp @_, ' in ' , (caller(1))[3] ;
77             } elsif ($Emit eq 'croak') {
78 2         432 croak @_, ' in ' , (caller(1))[3];
79             } elsif ($Emit eq 'print') {
80 5         331 print @_;
81             } elsif (ref $Emit =~ /CODE/) {
82 0         0 &$Emit(@_);
83             } else { # Otherwise it's a filehandle
84 1         98 print $Emit @_;
85             }
86             }
87              
88             sub emit_type {
89 1     1 0 2 my $orig = shift;
90 1         2 my $emit = shift;
91              
92 1 50       7 if ($emit =~ /(carp|croak|print)/) {
93 1         5 return "\$Devel::StealthDebug::Emit = $emit;$orig"
94             }
95             }
96              
97             sub add_assert {
98 0     0 0 0 my $orig = shift;
99 0         0 my $cond = my $cond2 = shift;
100              
101 0         0 $cond2 =~ s/\'/\\\'/g;
102 0         0 return "die '($cond2) condition failed' if !($cond);$orig";
103             }
104              
105             sub add_emit {
106 6     6 0 22 my $orig = shift;
107 6         15 my $text = shift;
108              
109 6         13 $text =~ s/^"(.*)"$/$1/;
110 6         13 $text =~ s/\"/\\\"/g;
111 6         42 return "$orig;Devel::StealthDebug::emit \"$text\";";
112             }
113              
114             sub add_dump {
115 2     2 0 12 my $orig = shift;
116 2         5 my $ref = shift;
117              
118 2         3 $Counter++;
119              
120 2         5 my $output = $orig . ';$Data::Dumper::Sortkeys=1;Devel::StealthDebug::emit(';
121 2         10 my @vars = split (/\s*,\s*/, $ref) ;
122 2         4 my $i = 0 ;
123              
124 2 100       7 if ($DUMPER) {
125 1         7 while ($vars[0] =~ /^\'|\"/) {
126 0 0       0 $output .= (shift @vars) . (@vars?',':'') ;
127             }
128 1         15 $output .= 'Data::Dumper -> Dump ([' . join (',', @vars) . "],['" . join ("','", @vars) . "']));";
129             } else {
130 1         3 foreach my $var (@vars) {
131 1 50       6 $output .= ($i++?',':'') . "'\$$var = ', Devel::StealthDebug::dumpvalue($var,-1)";
132             }
133 1         2 $output .= ');' ;
134             }
135 2         10 return $output ;
136             }
137              
138             sub dumpvalue {
139 10     10 0 785 my $type = shift;
140 10         16 my $tabn = shift;
141            
142 10         16 my $ref = ref $type;
143 10         21 my $tab = ' ' x ($tabn+1);
144 10         13 my $output;
145 10         13 $tabn += $TABLEN;
146              
147 10 100       186 if ($type =~ /^($ref=)?HASH/) {
    100          
    50          
148 2         9 $output = "{\n".dump_hash($type,$tabn,'')."$tab},\n";
149             } elsif ($type =~ /^($ref=)?ARRAY/) {
150 1         6 $output = "[\n".dump_array($type,$tabn,'')."$tab],\n";
151             } elsif ($type =~ /^($ref=)?SCALAR/) {
152 0         0 $output = dump_scalar($type,$tabn,'');
153             } else {
154 7         15 $output = dump_scalar($type,$tabn,'');
155             }
156              
157 10 100       27 if (($tabn - $TABLEN) == -1) {
158 1         4 $output =~ s/,$/;/s;
159             }
160              
161 10         35 return $output;
162             }
163              
164             sub dump_hash {
165 2     2 0 5 my $var = shift;
166 2         24 my $tabn = shift;
167 2         5 my $output = shift;
168            
169 2         5 my $tab = " " x $tabn;
170             #$output .= "$tab\n";
171 2         4 $tab .= " ";
172              
173 2         13 for my $elem (sort keys %$var) {
174 6 50       13 if (ref $elem) {
175 0         0 $output .= "$tab$elem => {"
176             } else {
177 6         14 $output .= "$tab'$elem' => ";
178             }
179 6         17 $output .= dumpvalue($var->{$elem},$tabn);
180             }
181 2         7 $output =~ s/\,$//s; # To remove the last ',' from the list
182            
183 2         8 return $output;
184             }
185              
186             sub dump_scalar {
187 7     7 0 10 my $scalar = shift;
188 7         9 my $tabn = shift;
189 7         18 my $output = shift;
190              
191 7 100       27 if ($scalar !~ /\d+/) { $scalar = "'$scalar'" }
  5         10  
192              
193 7         11 $output .= "$scalar,\n";
194            
195 7         17 return $output;
196             }
197              
198             sub dump_array {
199 1     1 0 2 my $var = shift;
200 1         3 my $tabn = shift;
201 1         2 my $output = shift;
202            
203 1         1 my $i;
204              
205 1         4 my $tab = " " x $tabn;
206             #$output .= "$tab\n";
207             #$output .= "$tab";
208 1         2 $tab .= " ";
209              
210 1         2 for my $elem (@$var) {
211 3         6 $output .= $tab;
212             #$output .= $i++;
213             #$output .= " => ";
214 3         9 $output .= dumpvalue($elem,$tabn);
215             }
216 1         5 $output =~ s/\,$//s; # To remove the last ',' from the list
217            
218 1         5 return $output;
219             }
220              
221             sub add_when {
222 1     1 0 2 my $orig = shift;
223 1         2 my $var = shift;
224 1         2 my $op = shift;
225 1         2 my $value = shift;
226              
227 1         1 push @{$Wait_Cond{$var}},[$op,$value];
  1         5  
228 1         3 return "$orig";
229             }
230              
231             sub add_watch {
232 7     7 0 20 my $orig = shift;
233 7         16 my $var = my $var2 = shift;
234              
235 7         24 $var2 =~ s/[\$\@\%]//;
236              
237 7         10 my ($pre,$post,$init);
238              
239 7 50       49 if ($orig =~ /\s*my\s*[\@\$\%]/) {
240 7         8 $pre = $orig;
241 7         227 $pre =~ s/(\s*my\s*[\@\$\%]$var2).*/$1;/i;
242             }
243            
244 7 100       42 if ($orig =~ /(=|\+\+|--)/) {
245 3         7 $post = $orig;
246 3         56 $post =~ s/.*([\@\$\%]$var2)/$1/si;
247             }
248              
249 7 0 33     22 $init = ",\\$var" if (!$pre && !$post) ;
250            
251 7         59 return "$pre tie $var,'Devel::StealthDebug','$var'$init;$post";
252             }
253              
254             sub check_when_cond {
255 15     15 0 16 my $object = shift;
256 15         17 my $value = shift;
257 15         20 my $index = shift;
258            
259 15         15 my $ok;
260 15         15 for my $cond (@{$Wait_Cond{$object->{name}}}) {
  15         49  
261             {
262 3         4 local ($@, $!);
  3         11  
263 3         199 $ok = eval "\$object->{value} $$cond[0] $$cond[1]";
264             }
265            
266 3 100       16 if ($ok) {
267 1         6 emit "$object->{name}$$cond[0]$$cond[1] !";
268             }
269             }
270             }
271              
272             FILTER {
273             #
274             # Make it consistent and CLEAN !
275             # (Of course if it could work...)
276             #
277             # Should we really forbid pure comment lines
278             #
279             if ($ENABLE) {
280             s/^([^#]*?)(#.*?!assert\((.+?)\)!)/add_assert($1,$3)/meg;
281             s/^([^#]*?)(#.*?!watch\((.+?)\)!)/add_watch($1,$3)/meg;
282             s/^([^#]*?)(#.*?!emit\((.+?)\)!)/add_emit($1,$3)/meg;
283             s/^([^#]*?)(#.*?!dump\((.+?)\)!)/add_dump($1,$3)/meg;
284             s/^([^#]*?)(#.*?!when\((.+?),(.+?),(.+?)\)!)/add_when($1,$3,$4,$5)/meg;
285             s/^([^#]*?)(#.*?!emit_type\((.+?)\)!)/emit_type($1,$3)/meg;
286             }
287             if ($SOURCE) { print SOURCE "$_\n" } ;
288             #s/(.)/$1/mg;
289             };
290              
291             sub TIESCALAR {
292 3     3   35 my $class = shift;
293 3         6 my $name = shift;
294 3         6 my $value = shift;
295 3         6 my %object;
296              
297 3         6 $object{name}=$name;
298 3 50       11 $object{value}=$$value if ($value) ;
299 3         14 bless \%object,$class;
300             }
301              
302             sub FETCH {
303 15     15   74 my $object = shift;
304 15         20 my $index = shift;
305              
306 15 100       81 if ($object->{name} =~ /^\@/) {
    100          
    50          
307 4         382 carp "FETCH ($object->{name}\[$index\] -> ",$object->{value}[$index],")";
308 4         164 return $object->{value}[$index];
309             } elsif ($object->{name} =~ /^\$/) {
310 7         728 carp "FETCH ($object->{name} -> ",$object->{value},")";
311 7         240 return $object->{value};
312             } elsif ($object->{name} =~ /^\%/) {
313 4         396 carp "FETCH ($object->{name}\{$index\} -> ",$object->{value}{$index},")";
314 4         167 return $object->{value}{$index};
315             } else {
316 0         0 carp "Strange FETCH"
317             }
318             }
319              
320             sub FETCHSIZE {
321 0     0   0 my $object = shift;
322 0         0 my $value = shift;
323              
324 0         0 $#{$object->{value}}=$value;
  0         0  
325 0         0 carp "FETCHSIZE ($object->{name})($value)";
326             }
327              
328             sub STORE {
329 15     15   56 my $object = shift;
330 15         23 my $value = pop;
331 15         21 my $index = shift;
332              
333 15 100       99 if ($object->{name} =~ /^\@/) {
    100          
    50          
334 4         9 $object->{value}[$index]=$value;
335 4         8 check_when_cond($object,$value,$index);
336 4         369 carp "STORE ($object->{name}\[$index\] <- $object->{value}[$index])";
337 4         164 return $object->{value}[$index];
338             } elsif ($object->{name} =~ /^\$/) {
339 7         13 $object->{value}=$value;
340 7         18 check_when_cond($object,$value,$index);
341 6         735 carp "STORE ($object->{name} <- $object->{value})";
342 6         228 return $object->{value};
343             } elsif ($object->{name} =~ /^\%/) {
344 4         11 $object->{value}{$index}=$value;
345 4         12 check_when_cond($object,$value,$index);
346 4         579 carp "STORE ($object->{name}\{$index\} <- $object->{value}{$index})";
347 4         294 return $object->{value}{$index};
348             }
349             }
350              
351             sub CLEAR {
352 0     0   0 my $object = shift;
353            
354 0         0 $object->{value}=[];
355 0         0 carp "CLEAR ($object->{name})";
356             }
357              
358             sub TIEARRAY {
359 2     2   17 my $class = shift;
360 2         4 my $name = shift;
361 2         3 my $value = shift;
362 2         4 my %object;
363              
364 2         44 $object{name} = $name;
365 2 50       9 $object{value}= $value?$value:[];
366 2         9 bless \%object,$class;
367             }
368              
369             sub TIEHASH {
370 2     2   1367 my $class = shift;
371 2         5 my $name = shift;
372 2         4 my $value = shift;
373 2         4 my %object;
374              
375 2         5 $object{name} = $name;
376 2 50       11 $object{value}= $value?$value:{};
377 2         10 bless \%object,$class;
378             }
379              
380             sub DELETE {
381 0     0   0 my $object = shift;
382 0         0 my $key = shift;
383              
384 0         0 delete $object->{value}{$key};
385 0         0 carp "DELETE ($object->{name})($key)";
386             }
387              
388             sub EXISTS {
389 0     0   0 my $object = shift;
390 0         0 my $key = shift;;
391              
392 0         0 carp "EXISTS ($object->{name})($key)";
393              
394 0 0       0 return 0 if $object->{value}{$key};
395 0         0 return 1;
396             }
397              
398             sub FIRSTKEY {
399 0     0   0 my $object = shift;
400 0         0 my $toreseteach = keys %{$object->{value}};
  0         0  
401              
402 0         0 $object->{lastkey} = each %{$object->{value}};
  0         0  
403 0         0 carp "FIRSTKEY ($object->{name})(",$object->{lastkey},")";
404 0         0 return $object->{lastkey}
405             }
406              
407             sub NEXTKEY {
408 0     0   0 my $object = shift;
409 0         0 my $key = shift;
410 0         0 my $lastkey = shift;
411              
412 0         0 carp "NEXTKEY ($object->{name})($key)($lastkey)";
413 0         0 return each %{$object->{value}}
  0         0  
414             }
415              
416             sub DESTROY {
417 7     7   15303 my $object = shift;
418              
419 7         5262 carp "DESTROY ($object->{name})";
420             }
421              
422              
423             sub STORESIZE {
424 0     0     my $object = shift;
425 0           my $count = shift;
426              
427 0           carp "STORESIZE ($object)($count)";
428             }
429              
430             sub PUSH {
431 0     0     my $object = shift;
432 0           my @list = @_;
433              
434 0           push @{$object->{value}},@list;
  0            
435 0           carp "PUSH ($object)(@list)";
436             }
437              
438             sub POP {
439 0     0     my $object = shift;
440 0           my $value = pop @{$object->{value}};
  0            
441              
442 0           carp "POP ($object)($value)";
443             }
444              
445             sub SHIFT {
446 0     0     my $object = shift;
447 0           my $value = shift @{$object->{value}};
  0            
448              
449 0           carp "SHIFT ($object)($value)";
450             }
451              
452             sub UNSHIFT {
453 0     0     my $object = shift;
454 0           my @list = @_;
455              
456 0           unshift @{$object->{value}},@list;
  0            
457 0           carp "SHIFT ($object)(@list)";
458             }
459              
460             sub SPLICE {
461 0     0     my $object = shift;
462 0           my $offset = shift;
463 0           my $length = shift;
464 0           my @list = @_;
465              
466 0           return splice @{$object->{value}},$offset,$length,@list
  0            
467             }
468              
469             sub EXTEND {
470 0     0     my $object = shift;
471 0           my $count = shift;
472              
473             # Nothing to do ?
474 0           carp "EXTEND (",$object->STORESIZE,")";
475             }
476              
477             1;
478              
479             __END__