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 : |