File Coverage

blib/lib/Perl/Critic/DynamicPolicy.pm
Criterion Covered Total %
statement 46 47 97.8
branch 13 18 72.2
condition 2 3 66.6
subroutine 8 9 88.8
pod 3 3 100.0
total 72 80 90.0


line stmt bran cond sub pod time code
1             ##############################################################################
2             # $URL: http://perlcritic.tigris.org/svn/perlcritic/tags/Perl-Critic-Dynamic-0.05/lib/Perl/Critic/DynamicPolicy.pm $
3             # $Date: 2010-09-24 12:32:37 -0700 (Fri, 24 Sep 2010) $
4             # $Author: thaljef $
5             # $Revision: 3935 $
6             ##############################################################################
7              
8             package Perl::Critic::DynamicPolicy;
9              
10 34     34   203 use strict;
  34         67  
  34         986  
11 34     34   137 use warnings;
  34         67  
  34         718  
12 34     34   39762 use Storable qw();
  34         131455  
  34         990  
13 34     34   273 use Carp qw(confess);
  34         69  
  34         2300  
14 34     34   1252 use English qw(-no_match_vars);
  34         3093  
  34         336  
15              
16 34     34   17272 use base 'Perl::Critic::Policy';
  34         100  
  34         37743  
17              
18             #-----------------------------------------------------------------------------
19              
20             our $VERSION = 0.05;
21              
22             #-----------------------------------------------------------------------------
23             # This function creates a pipe and forks. The child will compile the code and
24             # find violations. The violations are then serlialized and then sent back to
25             # the parent across the pipe. Meanwhile, the parent just waits for the child
26             # to report back.
27              
28             sub violates {
29              
30 560     560 1 7993 my ($self, $doc, $elem) = @_;
31              
32             # Open a pipe, and fork
33 560         97562 pipe my ($parent_reader, $child_writer);
34 560 50       2440225 defined (my $pid = fork) or confess 'Fork error';
35              
36              
37 560 100       406342 if (!$pid) {
38              
39             # child process
40 32         12592 my $eval = eval {
41              
42 32 50       6641 close $parent_reader or
43             confess "Failed to close unused pipe end: $OS_ERROR";
44              
45 32         1283 binmode $child_writer;
46              
47 32         5975 my @violations = $self->violates_dynamic($doc, $elem);
48 31         949 my $serialized = Storable::freeze(\@violations);
49 31         7681 print {$child_writer} $serialized;
  31         375  
50              
51 31 50       32148 close $child_writer
52             or confess "Failed to close pipe writer: $OS_ERROR";
53              
54 31         253 1;
55             };
56              
57             # All exceptions from the child process are caught. We communicate
58             # failure back to the parent via the exit $status of the child.
59             # The contents of $EVAL_ERROR will be written to STDERR, but at
60             # the moment, the parent just ignores it.
61              
62 32 100 66     1045 my $status = (!$eval || $EVAL_ERROR) ? 1 : 0;
63 32 100       260 warn "$EVAL_ERROR\n" if $status;
64 32         29783 exit $status;
65             }
66              
67              
68             # parent (i.e. original) process
69 528 50       82440 close $child_writer or confess "Failed to close unused pipe end: $OS_ERROR";
70 528         19398 binmode $parent_reader;
71              
72 528         39283 my $serialized = do {local $INPUT_RECORD_SEPARATOR = undef; <$parent_reader>};
  528         38023  
  528         24220994  
73 528 50       35444 close $parent_reader or confess "Failed to close pipe reader: $OS_ERROR";
74 528         583588509 waitpid $pid, 0; # pause until child process exits
75              
76             # Here is where the parent detects failure from the child. But at this
77             # point, we don't know why the child failed.
78              
79 528 100       23400 confess "Child process had errors. Status: $CHILD_ERROR" if $CHILD_ERROR;
80 518         4373 my @violations = @{Storable::thaw($serialized)};
  518         20109  
81 518         113879 return @violations;
82             }
83              
84             #-----------------------------------------------------------------------------
85              
86 1     1 1 1004 sub is_safe { return 0; }
87              
88             #-----------------------------------------------------------------------------
89              
90 0     0 1   sub violates_dynamic { confess q{Can't call abstract method}; }
91              
92             #-----------------------------------------------------------------------------
93              
94             1;
95              
96             __END__
97              
98             =pod
99              
100             =head1 NAME
101              
102             Perl::Critic::DynamicPolicy - Base class for dynamic Policies
103              
104             =head1 DESCRIPTION
105              
106             L<Perl::Critic::DynamicPolicy> is intended to be used as a base class for
107             L<Perl::Critic::Policy> modules that wish to compile and/or execute the code
108             that is being analyzed.
109              
110             Policies that inherit from L<Perl::Critic::DynamicPolicy> will C<fork> the
111             process each time the C<violates> method is called. The child process is then
112             free to compile the code and do other mischievous things without corrupting
113             the symbol table of the parent process. When the analysis is complete, the
114             child serializes any L<Perl::Critic::Violation> objects that were created and
115             sends them back to the parent across a pipe.
116              
117             Any Policy that inherits from L<Perl::Critic::DynamicPolicy> will also be
118             marked as "unsafe" and is usually ignored by both L<Perl::Critic> and
119             L<perlcritic>. To use a Policy that inherits from
120             L<Perl::Critic::DynamicPolicy>, you must set the C<-allow-unsafe> switch in
121             the L<Perl::Critic> constructor or on the L<perlcritic> command line.
122              
123             In every other way, a L<Perl::Critic::DynamicPolicy> behaves just like an
124             ordinary L<Perl::Critic::Policy>. For Policy authors, the main difference is
125             that you must override the C<violates_dynamic> method instead of the
126             C<violates> method. See L<Perl::Critic::DEVELOPER> for a discussion of the
127             other aspects of creating new Policies.
128              
129             =head1 METHODS
130              
131             This list of methods is not exhaustive. It only covers the methods that are
132             uniquely relevant to L<Perl::Critic::DynamicPolicy> subclasses. See
133             L<Perl::Critic::Policy> and L<Perl::Critic::DEVELOPER> for documentation about
134             the other methods shared by all Policies.
135              
136             =over
137              
138             =item C< violates( $doc, $elem ) >
139              
140             In a typical L<Perl::Critic::Policy> subclass, you would override the
141             C<violates> method to do whatever code analysis you want. But with
142             L<Perl::Critic::DynamicPolicy>, this method has already been overridden to
143             perform the necessary pipe and fork operations that I described above. So
144             instead, you need to override the C<violates_dyanmic> method.
145              
146             =item C< violates_dynamic( $doc, $elem ) >
147              
148             Given a PPI::Element and a PPI::Document, returns one or more
149             L<Perl::Critic::Violation> objects if the C<$elem> or <$doc> violates this
150             Policy. If there are no violations, then it returns an empty list. This
151             method will be called in a child process, so you can compile C<$doc> without
152             interfering with the parent process.
153              
154             C<violates_dynamic> is an abstract method and it will abort if you attempt to
155             invoke it directly. It is the heart of your L<Perl::Critic::DynamicPolicy>
156             modules, and your subclass must override this method.
157              
158             =item C< is_safe() >
159              
160             Returns false. Any Policy derived from this module is presumed to be unsafe.
161             L<Perl::Critic> and L<perlcritic> users can only load Policies derived from
162             this module if they use the C<-allow-unsafe> switch.
163              
164             =back
165              
166             =head1 AUTHOR
167              
168             Jeffrey Ryan Thalhammer <thaljef@cpan.org>
169              
170             =head1 COPYRIGHT
171              
172             Copyright (c) 2007 Jeffrey Ryan Thalhammer. All rights reserved.
173              
174             This program is free software; you can redistribute it and/or modify it under
175             the same terms as Perl itself. The full text of this license can be found in
176             the LICENSE file included with this module.
177              
178             =cut
179              
180             ##############################################################################
181             # Local Variables:
182             # mode: cperl
183             # cperl-indent-level: 4
184             # fill-column: 78
185             # indent-tabs-mode: nil
186             # c-indentation-style: bsd
187             # End:
188             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab :