line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PPI::Xref; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
require v5.14; # 5.10: defined-or; 5.14: package Sub { ... } |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.008'; |
6
|
|
|
|
|
|
|
|
7
|
32
|
|
|
32
|
|
665775
|
use strict; |
|
32
|
|
|
|
|
77
|
|
|
32
|
|
|
|
|
860
|
|
8
|
32
|
|
|
32
|
|
179
|
use warnings; |
|
32
|
|
|
|
|
100
|
|
|
32
|
|
|
|
|
815
|
|
9
|
|
|
|
|
|
|
|
10
|
32
|
|
|
32
|
|
26365
|
use PPI; |
|
32
|
|
|
|
|
4945452
|
|
|
32
|
|
|
|
|
9972
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# We will use file ids (integers) instead of filenames, mainly for |
13
|
|
|
|
|
|
|
# space savings, but also speed, and only convert back to filenames |
14
|
|
|
|
|
|
|
# on leaving the API boundary. |
15
|
|
|
|
|
|
|
my $FILE_ID = 0; |
16
|
|
|
|
|
|
|
my %FILE_BY_ID; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my %CTOR_OPTS = |
19
|
|
|
|
|
|
|
map { $_ => 1} qw/process_verbose cache_verbose recurse_verbose |
20
|
|
|
|
|
|
|
recurse INC |
21
|
|
|
|
|
|
|
cache_directory |
22
|
|
|
|
|
|
|
__allow_relative/; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $HASHALGO = 'sha1'; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
package Sub { # For getting the current sub name. |
27
|
32
|
|
|
32
|
|
192
|
sub TIESCALAR { bless \$_[1], $_[0] } |
28
|
119
|
|
|
119
|
|
2942
|
sub FETCH { (caller(1))[3] } |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
tie my $Sub, 'Sub'; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub __is_readwrite_directory { |
33
|
72
|
|
|
72
|
|
123
|
my ($self, $dir) = @_; |
34
|
72
|
|
33
|
|
|
3302
|
return -d $dir && -r $dir && -w $dir; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Our constructor. |
38
|
|
|
|
|
|
|
sub new { |
39
|
37
|
|
|
37
|
0
|
39440
|
my ($class, $opt) = @_; |
40
|
37
|
|
100
|
|
|
187
|
$opt //= {}; |
41
|
|
|
|
|
|
|
# In the opt you can specify: |
42
|
|
|
|
|
|
|
# - process_verbose: for process progress |
43
|
|
|
|
|
|
|
# - cache_verbose: for cache activity |
44
|
|
|
|
|
|
|
# - recurse_verbose: for recurse, show revisits |
45
|
|
|
|
|
|
|
# - INC: an aref for a custom @INC |
46
|
|
|
|
|
|
|
# - recurse: or not (default: yes) |
47
|
|
|
|
|
|
|
# - cache_directory: directory where to cache the results |
48
|
|
|
|
|
|
|
|
49
|
37
|
|
100
|
|
|
275
|
$opt->{recurse} //= 1; |
50
|
|
|
|
|
|
|
|
51
|
37
|
|
|
|
|
195
|
my %unexpopt = %$opt; |
52
|
37
|
|
|
|
|
229
|
delete @unexpopt{keys %CTOR_OPTS}; |
53
|
|
|
|
|
|
|
|
54
|
37
|
|
|
|
|
199
|
for my $k (sort keys %unexpopt) { |
55
|
0
|
|
|
|
|
0
|
warn "$Sub: unexpected option: $k\n"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
37
|
|
|
|
|
189
|
my $self = { opt => $opt }; |
59
|
|
|
|
|
|
|
|
60
|
37
|
|
|
|
|
113
|
my $cache_directory = $opt->{cache_directory}; |
61
|
37
|
100
|
|
|
|
194
|
if (defined $cache_directory) { |
62
|
8
|
50
|
|
|
|
45
|
unless (PPI::Xref->__is_readwrite_directory($cache_directory)) { |
63
|
0
|
|
|
|
|
0
|
warn "$Sub: cache_directory '$cache_directory': not a read-write directory\n"; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
8
|
|
|
|
|
30
|
$self->{__cache_prefix_length} = length($cache_directory) + 1; |
67
|
|
|
|
|
|
|
|
68
|
32
|
|
|
32
|
|
310
|
use Sereal::Encoder; |
|
32
|
|
|
|
|
76
|
|
|
32
|
|
|
|
|
1530
|
|
69
|
32
|
|
|
32
|
|
174
|
use Sereal::Decoder; |
|
32
|
|
|
|
|
64
|
|
|
32
|
|
|
|
|
8836
|
|
70
|
8
|
|
|
|
|
139
|
$self->{encoder} = Sereal::Encoder->new; |
71
|
8
|
|
|
|
|
92
|
$self->{decoder} = Sereal::Decoder->new; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
37
|
|
|
|
|
171
|
bless $self, $class; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Unless $self->{inc_dirs} is set, set it from either opt INC, if set, |
78
|
|
|
|
|
|
|
# or from the system @INC. |
79
|
|
|
|
|
|
|
sub __inc_dirs { |
80
|
180
|
|
|
180
|
|
288
|
my $self = shift; |
81
|
180
|
100
|
|
|
|
722
|
unless (defined $self->{inc_dirs}) { |
82
|
36
|
100
|
|
|
|
167
|
for my $d ($self->{opt}{INC} ? @{ $self->{opt}{INC}} : @INC) { |
|
35
|
|
|
|
|
149
|
|
83
|
76
|
50
|
|
|
|
207
|
next if ref $d; |
84
|
76
|
50
|
33
|
|
|
2563
|
next unless -d $d && -x $d; |
85
|
76
|
|
|
|
|
112
|
push @{ $self->{inc_dirs} }, $d; |
|
76
|
|
|
|
|
289
|
|
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub PPI::Xref::INC { |
91
|
36
|
|
|
36
|
1
|
211
|
my $self = shift; |
92
|
36
|
|
|
|
|
147
|
$self->__inc_dirs; |
93
|
36
|
|
|
|
|
222
|
return $self->{inc_dirs}; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# Given a file, look for it in @INC. |
97
|
|
|
|
|
|
|
sub __find_file { |
98
|
144
|
|
|
144
|
|
256
|
my ($self, $file) = @_; |
99
|
144
|
|
|
|
|
375
|
$self->__inc_dirs; |
100
|
144
|
50
|
|
|
|
454
|
unless (exists $self->{inc_file}{$file}) { |
101
|
144
|
|
|
|
|
208
|
for my $d (@{ $self->{inc_dirs}}) { |
|
144
|
|
|
|
|
361
|
|
102
|
295
|
50
|
|
|
|
808
|
unless ($self->{opt}{__allow_relative}) { # For testing. |
103
|
32
|
|
|
32
|
|
178
|
use File::Spec; |
|
32
|
|
|
|
|
67
|
|
|
32
|
|
|
|
|
28429
|
|
104
|
0
|
0
|
|
|
|
0
|
$d = File::Spec->rel2abs($d) unless |
105
|
|
|
|
|
|
|
File::Spec->file_name_is_absolute($d); |
106
|
|
|
|
|
|
|
} |
107
|
295
|
|
|
|
|
633
|
my $f = "$d/$file"; |
108
|
295
|
100
|
|
|
|
6668
|
if (-f $f) { |
109
|
140
|
|
|
|
|
440
|
$self->{inc_file}{$file} = $f; |
110
|
140
|
|
|
|
|
274
|
last; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
} |
113
|
144
|
100
|
|
|
|
472
|
unless (exists $self->{inc_file}{$file}) { |
114
|
4
|
|
|
|
|
10
|
$self->{inc_file}{$file} = undef; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
144
|
|
|
|
|
464
|
return $self->{inc_file}{$file}; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Given a module name, look for its file in @INC. |
121
|
|
|
|
|
|
|
sub __find_module { |
122
|
173
|
|
|
173
|
|
302
|
my ($self, $module_name) = @_; |
123
|
173
|
100
|
|
|
|
538
|
unless (exists $self->{module_file}{$module_name}) { |
124
|
94
|
|
|
|
|
158
|
my $m = $module_name; |
125
|
94
|
|
|
|
|
180
|
$m =~ s{::}{/}g; |
126
|
94
|
|
|
|
|
138
|
$m .= '.pm'; |
127
|
94
|
|
|
|
|
265
|
$self->{module_file}{$module_name} = $self->__find_file($m); |
128
|
|
|
|
|
|
|
} |
129
|
173
|
|
|
|
|
436
|
return $self->{module_file}{$module_name}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Remove comments and tokens, and squeeze |
133
|
|
|
|
|
|
|
# multiple whitespaces into one. |
134
|
|
|
|
|
|
|
sub __normalize_whitespace { |
135
|
2230
|
|
|
2230
|
|
2732
|
my @n; |
136
|
|
|
|
|
|
|
my $prev_ws; |
137
|
0
|
|
|
|
|
0
|
my $curr_ws; |
138
|
2230
|
|
|
|
|
3897
|
for my $n (@_) { |
139
|
12226
|
100
|
|
|
|
47332
|
next if $n->isa('PPI::Token::Comment'); |
140
|
12096
|
100
|
|
|
|
44775
|
next if $n->isa('PPI::Token::Pod'); |
141
|
12083
|
|
|
|
|
33171
|
$curr_ws = $n->isa('PPI::Token::Whitespace'); |
142
|
12083
|
100
|
100
|
|
|
34540
|
next if $prev_ws && $curr_ws; |
143
|
11456
|
|
|
|
|
13823
|
push @n, $n; |
144
|
11456
|
|
|
|
|
18002
|
$prev_ws = $curr_ws; |
145
|
|
|
|
|
|
|
} |
146
|
2230
|
|
|
|
|
6664
|
return @n; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# For a filename, assign it a file id (an integer) if it does not |
150
|
|
|
|
|
|
|
# have one, and in any case return its file id. |
151
|
|
|
|
|
|
|
sub __assign_file_id { |
152
|
200
|
|
|
200
|
|
339
|
my ($self, $filename) = @_; |
153
|
200
|
|
|
|
|
452
|
my $file_id = $self->{file_id}{$filename}; |
154
|
200
|
100
|
|
|
|
559
|
unless (defined $file_id) { |
155
|
182
|
|
|
|
|
453
|
$file_id = $self->{file_id}{$filename} = $FILE_ID++; |
156
|
182
|
|
|
|
|
519
|
$FILE_BY_ID{$file_id} = $filename; |
157
|
|
|
|
|
|
|
} |
158
|
200
|
|
|
|
|
503
|
return $file_id; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# Close the current package, if any. |
162
|
|
|
|
|
|
|
sub __close_package { |
163
|
330
|
|
|
330
|
|
651
|
my ($self, $file_id, $package, $elem) = @_; |
164
|
330
|
100
|
100
|
|
|
1900
|
if (exists $self->{file_packages} && |
|
|
|
66
|
|
|
|
|
165
|
|
|
|
|
|
|
ref $self->{file_packages}{$file_id} && |
166
|
273
|
|
|
|
|
1130
|
@{ $self->{file_packages}{$file_id} }) { |
167
|
273
|
|
|
|
|
428
|
push @{ $self->{file_packages}{$file_id}[-1] }, |
|
273
|
|
|
|
|
1036
|
|
168
|
|
|
|
|
|
|
$elem->line_number, |
169
|
|
|
|
|
|
|
$elem->column_number; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# Open a new package. |
174
|
|
|
|
|
|
|
sub __open_package { |
175
|
273
|
|
|
273
|
|
590
|
my ($self, $file_id, $package, $elem) = @_; |
176
|
273
|
|
|
|
|
323
|
push @{ $self->{file_packages}{$file_id} }, |
|
273
|
|
|
|
|
1149
|
|
177
|
|
|
|
|
|
|
[ |
178
|
|
|
|
|
|
|
$package, # 0 |
179
|
|
|
|
|
|
|
$elem->line_number, # 1 |
180
|
|
|
|
|
|
|
$elem->column_number, # 2 |
181
|
|
|
|
|
|
|
]; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Close the current package, if any, and open a new one. |
185
|
|
|
|
|
|
|
sub __close_open_package { |
186
|
156
|
|
|
156
|
|
349
|
my ($self, $file_id, $old_package, $old_elem, |
187
|
|
|
|
|
|
|
$new_package, $new_elem, $fileloc) = @_; |
188
|
156
|
100
|
66
|
|
|
750
|
if (defined $old_package && $old_package ne 'main') { |
189
|
154
|
|
|
|
|
367
|
$self->__close_package($file_id, $old_package, $old_elem, $fileloc); |
190
|
|
|
|
|
|
|
} |
191
|
156
|
|
|
|
|
3779
|
$self->__open_package($file_id, $new_package, $new_elem); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Function for portably turning the directory portion of a pathname |
195
|
|
|
|
|
|
|
# into a directory name. If the $flatten_volume is true, loses |
196
|
|
|
|
|
|
|
# information in platforms that have a volume name in pathnames, but |
197
|
|
|
|
|
|
|
# the main idea is to safely split the argument into a new directory |
198
|
|
|
|
|
|
|
# name (possibly modified by prepending the volume name as a |
199
|
|
|
|
|
|
|
# directory), and the filename. E.g. '/a/b/c' -> ('/a/b', 'c') |
200
|
|
|
|
|
|
|
# 'c:/d/e' -> ('/c/d', 'e') |
201
|
|
|
|
|
|
|
sub __safe_vol_dir_file { |
202
|
100
|
|
|
100
|
|
160
|
my ($self, $path, $flatten_volume) = @_; |
203
|
100
|
|
|
|
|
1433
|
my ($vol, $dirs, $file) = File::Spec->splitpath($path); |
204
|
100
|
50
|
66
|
|
|
516
|
if ($flatten_volume && $^O eq 'MSWin32') { |
205
|
0
|
|
|
|
|
0
|
$vol =~ s/:$//; # splitpath() leaves the $vol as e.g. "c:" |
206
|
|
|
|
|
|
|
} |
207
|
100
|
|
|
|
|
758
|
return (File::Spec->catpath($vol, $dirs), $file); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Returns the directory part and the file part. Note that this will |
211
|
|
|
|
|
|
|
# convert the volume name (if any) in the $path into a directory name, |
212
|
|
|
|
|
|
|
# e.g. 'c:/d/e' -> ('/c/d', 'e'). This is useful for re-rooting |
213
|
|
|
|
|
|
|
# a pathname under a new directory. |
214
|
|
|
|
|
|
|
sub __safe_dir_and_file_flatten_volume { |
215
|
64
|
|
|
64
|
|
104
|
my ($self, $path) = @_; |
216
|
64
|
|
|
|
|
150
|
return $self->__safe_vol_dir_file($path, 1); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# Returns the directory part (with the possible volume part prepended) |
220
|
|
|
|
|
|
|
# and the file part. Kind of like a safe dirname(). |
221
|
|
|
|
|
|
|
sub __safe_dir_and_file_same_volume { |
222
|
36
|
|
|
36
|
|
61
|
my ($self, $path) = @_; |
223
|
36
|
|
|
|
|
89
|
return $self->__safe_vol_dir_file($path, 0); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
my $CACHE_EXT = '.cache'; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# "shadow file" is a filename rooted into a new, "shadow", directory. |
229
|
|
|
|
|
|
|
sub __shadow_cache_filename { |
230
|
64
|
|
|
64
|
|
120
|
my ($self, $shadowdir, $filename) = @_; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Paranoia check. (Either absolute or relative is fine, though.) |
233
|
64
|
50
|
|
|
|
181
|
if ($filename =~ m{\.\.}) { |
234
|
0
|
|
|
|
|
0
|
warn "$Sub: Skipping unexpected file: '$filename'\n"; |
235
|
0
|
|
|
|
|
0
|
return; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
32
|
|
|
32
|
|
189
|
use File::Spec; |
|
32
|
|
|
|
|
62
|
|
|
32
|
|
|
|
|
6500
|
|
239
|
64
|
50
|
|
|
|
609
|
my $absfile = |
240
|
|
|
|
|
|
|
File::Spec->file_name_is_absolute($filename) ? |
241
|
|
|
|
|
|
|
$filename : |
242
|
|
|
|
|
|
|
File::Spec->rel2abs($filename); |
243
|
64
|
|
|
|
|
188
|
my ($redir, $file) = $self->__safe_dir_and_file_flatten_volume($absfile); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# For portable filenames, we cannot just keep on |
246
|
|
|
|
|
|
|
# appending filename extensions with dots, and we |
247
|
|
|
|
|
|
|
# are going to append the cache filename extension. |
248
|
|
|
|
|
|
|
# So we mangle the .pm or .pl as _pm and _pl. |
249
|
64
|
|
|
|
|
410
|
$file =~ s{\.(p[ml])$}{_$1}; |
250
|
|
|
|
|
|
|
|
251
|
64
|
|
|
|
|
929
|
return File::Spec->catfile($shadowdir, $redir, $file . $CACHE_EXT); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# The hash checksum for the file, and the mtime timestamp. |
255
|
|
|
|
|
|
|
sub __current_filehash_and_mtime { |
256
|
188
|
|
|
188
|
|
286
|
my ($self, $origfilename) = @_; |
257
|
188
|
100
|
|
|
|
4298
|
return unless -f $origfilename; |
258
|
187
|
|
|
|
|
262
|
my $origfilefh; |
259
|
187
|
50
|
|
|
|
7725
|
unless (open($origfilefh, $origfilename)) { |
260
|
0
|
|
|
|
|
0
|
warn qq[$Sub: Failed to open "$origfilename": $!\n]; |
261
|
0
|
|
|
|
|
0
|
return; |
262
|
|
|
|
|
|
|
} |
263
|
32
|
|
|
32
|
|
28502
|
use Digest::SHA; |
|
32
|
|
|
|
|
123310
|
|
|
32
|
|
|
|
|
5404
|
|
264
|
187
|
|
|
|
|
1456
|
my $sha = Digest::SHA->new($HASHALGO); |
265
|
187
|
|
|
|
|
3890
|
$sha->addfile($origfilefh); |
266
|
|
|
|
|
|
|
return ( |
267
|
187
|
|
|
|
|
20724
|
"$HASHALGO:". $sha->hexdigest, |
268
|
|
|
|
|
|
|
(stat($origfilefh))[9], # mtime |
269
|
|
|
|
|
|
|
); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# Create the directory of the filename. |
273
|
|
|
|
|
|
|
sub __make_path_file { |
274
|
36
|
|
|
36
|
|
73
|
my ($self, $base) = @_; |
275
|
32
|
|
|
32
|
|
220
|
use File::Path qw[make_path]; |
|
32
|
|
|
|
|
65
|
|
|
32
|
|
|
|
|
9308
|
|
276
|
36
|
|
|
|
|
101
|
my ($dir, $file) = $self->__safe_dir_and_file_same_volume($base); |
277
|
36
|
100
|
|
|
|
77
|
return eval { make_path($dir) unless -d $dir; 1; }; |
|
36
|
|
|
|
|
5606
|
|
|
36
|
|
|
|
|
139
|
|
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# The attributes that are written to and read from cache. |
281
|
|
|
|
|
|
|
my @CACHE_FIELDS = |
282
|
|
|
|
|
|
|
qw[ |
283
|
|
|
|
|
|
|
file_incs |
284
|
|
|
|
|
|
|
file_lines |
285
|
|
|
|
|
|
|
file_modules |
286
|
|
|
|
|
|
|
file_packages |
287
|
|
|
|
|
|
|
file_subs |
288
|
|
|
|
|
|
|
file_missing_modules |
289
|
|
|
|
|
|
|
file_parse_errors |
290
|
|
|
|
|
|
|
]; |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Error fields are cache fields but they should not be cleared |
293
|
|
|
|
|
|
|
# since they accumulate and are hrefs as opposed to arefs. |
294
|
|
|
|
|
|
|
my %CACHE_FIELDS_KEEP = |
295
|
|
|
|
|
|
|
map { $_ => 1 } |
296
|
|
|
|
|
|
|
qw[ |
297
|
|
|
|
|
|
|
file_missing_modules |
298
|
|
|
|
|
|
|
file_parse_errors |
299
|
|
|
|
|
|
|
]; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
my @CACHE_FIELDS_CLEAR = |
302
|
|
|
|
|
|
|
grep { ! exists $CACHE_FIELDS_KEEP{$_} } @CACHE_FIELDS; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Given the href, serialize it to the file. |
305
|
|
|
|
|
|
|
sub __encode_to_file { |
306
|
36
|
|
|
36
|
|
73
|
my ($self, $file, $cached) = @_; |
307
|
|
|
|
|
|
|
|
308
|
36
|
|
|
|
|
46
|
my $success = 0; |
309
|
|
|
|
|
|
|
|
310
|
36
|
|
|
|
|
157
|
my $temp = "$file.$$"; # For atomic renaming. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# If anything goes wrong, abort the commit. |
313
|
|
|
|
|
|
|
COMMIT: { |
314
|
36
|
|
|
|
|
53
|
my $blob = $self->{encoder}->encode($cached); |
|
36
|
|
|
|
|
1878
|
|
315
|
36
|
50
|
|
|
|
114
|
unless (defined $blob) { |
316
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to encode into '$temp'\n"; |
317
|
0
|
|
|
|
|
0
|
last COMMIT; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
36
|
50
|
|
|
|
113
|
unless ($self->__make_path_file($temp)) { |
321
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to create path for '$temp'\n"; |
322
|
0
|
|
|
|
|
0
|
last COMMIT; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
36
|
|
|
|
|
60
|
my $fh; |
326
|
32
|
|
|
32
|
|
535
|
use Fcntl qw[O_CREAT O_WRONLY]; |
|
32
|
|
|
|
|
68
|
|
|
32
|
|
|
|
|
20321
|
|
327
|
36
|
50
|
|
|
|
3365
|
unless (sysopen($fh, $temp, O_CREAT|O_WRONLY, 0644)) { |
328
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to open '$temp' for writing: $!\n"; |
329
|
0
|
|
|
|
|
0
|
last COMMIT; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
36
|
|
|
|
|
89
|
my $size = length($blob); |
333
|
36
|
|
|
|
|
1370
|
my $wrote = syswrite($fh, $blob); |
334
|
|
|
|
|
|
|
|
335
|
36
|
50
|
33
|
|
|
231
|
unless (defined $wrote && $wrote == $size) { |
336
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to write $size bytes to '$temp': $!\n"; |
337
|
0
|
|
|
|
|
0
|
last COMMIT; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
36
|
50
|
|
|
|
532
|
unless (close($fh)) { |
341
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to close '$temp': $!\n"; |
342
|
0
|
|
|
|
|
0
|
last COMMIT; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
36
|
50
|
|
|
|
2475
|
unless (rename($temp, $file)) { |
346
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to rename '$temp' as '$file': !$\n"; |
347
|
0
|
|
|
|
|
0
|
last COMMIT; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# Finally we are happy. |
351
|
36
|
|
|
|
|
150
|
$success = 1; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
} # COMMIT |
354
|
|
|
|
|
|
|
|
355
|
36
|
50
|
|
|
|
832
|
if (-f $temp) { |
356
|
0
|
|
|
|
|
0
|
warn "$Sub: Cleaning temporary file '$temp'\n"; |
357
|
|
|
|
|
|
|
} |
358
|
36
|
|
|
|
|
612
|
unlink $temp; # In any case. |
359
|
|
|
|
|
|
|
|
360
|
36
|
|
|
|
|
200
|
return $success; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# Write the results to the file. |
364
|
|
|
|
|
|
|
sub __write_cachefile { |
365
|
36
|
|
|
36
|
|
73
|
my ($self, $cache_filename, $hash_current, $file_id, $file_mtime) = @_; |
366
|
|
|
|
|
|
|
|
367
|
36
|
100
|
|
|
|
146
|
if ($self->{opt}{cache_verbose}) { |
368
|
20
|
|
|
|
|
62
|
print "$Sub: writing $cache_filename\n"; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
36
|
|
|
|
|
68
|
my $cached; # Re-root the data we care about. |
372
|
36
|
|
|
|
|
82
|
for my $k (@CACHE_FIELDS) { |
373
|
252
|
100
|
|
|
|
766
|
if (defined $self->{$k}{$file_id}) { |
374
|
152
|
|
|
|
|
438
|
$cached->{$k} = $self->{$k}{$file_id}; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
} |
377
|
36
|
|
|
|
|
85
|
$cached->{file_hash} = $hash_current; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# The mtime is in UTC, and should only be used for |
380
|
|
|
|
|
|
|
# maintenance / statistics. In other words, it should |
381
|
|
|
|
|
|
|
# NOT be used for uptodateness. |
382
|
36
|
|
|
|
|
65
|
$cached->{file_mtime} = $file_mtime; |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# Mark also in the object that we have processed this one. |
385
|
36
|
|
|
|
|
90
|
$self->{file_hash}{$file_id} = $hash_current; |
386
|
|
|
|
|
|
|
|
387
|
36
|
|
|
|
|
112
|
return $self->__encode_to_file($cache_filename, $cached); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# Compose a cache filename, given an original filename. |
391
|
|
|
|
|
|
|
# The filenames are re-rooted in the cache_directory. |
392
|
|
|
|
|
|
|
sub __cache_filename { |
393
|
64
|
|
|
64
|
|
117
|
my ($self, $path) = @_; |
394
|
64
|
50
|
|
|
|
170
|
return if $path eq '-'; |
395
|
|
|
|
|
|
|
|
396
|
64
|
|
|
|
|
126
|
my $cache_directory = $self->{opt}{cache_directory}; |
397
|
64
|
50
|
|
|
|
169
|
return unless defined $cache_directory; |
398
|
|
|
|
|
|
|
|
399
|
64
|
50
|
|
|
|
177
|
unless ($self->__is_readwrite_directory($cache_directory)) { |
400
|
0
|
|
|
|
|
0
|
warn "$Sub: Not a read-write directory '$cache_directory'\n"; |
401
|
0
|
|
|
|
|
0
|
return; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
64
|
50
|
|
|
|
327
|
if ($path !~ /\.p[ml]$/) { |
405
|
0
|
|
|
|
|
0
|
warn "$Sub: Unexpected filename: '$path'\n"; |
406
|
0
|
|
|
|
|
0
|
return; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
64
|
|
|
|
|
172
|
return $self->__shadow_cache_filename($cache_directory, $path); |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Deserialize from the file. |
413
|
|
|
|
|
|
|
sub __decode_from_file { |
414
|
57
|
|
|
57
|
|
1261
|
my ($self, $file) = @_; |
415
|
|
|
|
|
|
|
|
416
|
57
|
|
|
|
|
72
|
my $fh; |
417
|
32
|
|
|
32
|
|
170
|
use Fcntl qw[O_RDONLY]; |
|
32
|
|
|
|
|
57
|
|
|
32
|
|
|
|
|
61388
|
|
418
|
57
|
100
|
|
|
|
2012
|
unless (sysopen($fh, $file, O_RDONLY)) { |
419
|
|
|
|
|
|
|
# warn "$Sub: Failed to open '$file' for reading: $!\n"; |
420
|
35
|
|
|
|
|
120
|
return; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
22
|
|
|
|
|
154
|
my $size = -s $fh; |
424
|
22
|
|
|
|
|
119
|
my $read = sysread($fh, my $blob, $size); |
425
|
22
|
50
|
|
|
|
57
|
unless ($read == $size) { |
426
|
0
|
|
|
|
|
0
|
warn "$Sub: Failed to read $size bytes from '$file': $!\n"; |
427
|
0
|
|
|
|
|
0
|
return; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
22
|
|
|
|
|
689
|
return $self->{decoder}->decode($blob); |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Check if we have the results for this file cached. |
434
|
|
|
|
|
|
|
sub __check_cached { |
435
|
187
|
|
|
187
|
|
302
|
my ($self, $origfile) = @_; |
436
|
187
|
50
|
|
|
|
470
|
return if $origfile eq '-'; |
437
|
|
|
|
|
|
|
|
438
|
187
|
|
|
|
|
545
|
my ($hash_current, $file_mtime) = |
439
|
|
|
|
|
|
|
$self->__current_filehash_and_mtime($origfile); |
440
|
187
|
|
|
|
|
575
|
my $cache_directory = $self->{opt}{cache_directory}; |
441
|
187
|
|
|
|
|
283
|
my $cache_filename; |
442
|
|
|
|
|
|
|
my $cached; |
443
|
0
|
|
|
|
|
0
|
my $hash_previous; |
444
|
0
|
|
|
|
|
0
|
my $hash_match; |
445
|
|
|
|
|
|
|
|
446
|
187
|
100
|
|
|
|
551
|
if (defined $cache_directory) { |
447
|
56
|
|
|
|
|
165
|
$cache_filename = $self->__cache_filename($origfile); |
448
|
56
|
50
|
|
|
|
194
|
if (defined $cache_filename) { |
449
|
56
|
100
|
|
|
|
154
|
if ($self->{opt}{cache_verbose}) { |
450
|
25
|
|
|
|
|
76
|
print "$Sub: reading $cache_filename\n"; |
451
|
|
|
|
|
|
|
} |
452
|
56
|
|
|
|
|
174
|
$cached = $self->__decode_from_file($cache_filename); |
453
|
56
|
100
|
|
|
|
149
|
if (defined $cached) { |
454
|
21
|
100
|
|
|
|
61
|
if ($self->{opt}{cache_verbose}) { |
455
|
5
|
|
|
|
|
16
|
print "$Sub: reading $cache_filename SUCCESS\n"; |
456
|
|
|
|
|
|
|
} |
457
|
21
|
|
|
|
|
41
|
$hash_previous = $cached->{file_hash}; |
458
|
21
|
|
66
|
|
|
156
|
$hash_match = |
459
|
|
|
|
|
|
|
defined $hash_previous && |
460
|
|
|
|
|
|
|
defined $hash_current && |
461
|
|
|
|
|
|
|
$hash_previous eq $hash_current; |
462
|
|
|
|
|
|
|
} else { |
463
|
35
|
100
|
|
|
|
116
|
if ($self->{opt}{cache_verbose}) { |
464
|
20
|
|
|
|
|
52
|
print "$Sub: reading $cache_filename FAILURE\n"; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
187
|
|
|
|
|
709
|
return ($cache_filename, |
471
|
|
|
|
|
|
|
$cached, |
472
|
|
|
|
|
|
|
$hash_current, |
473
|
|
|
|
|
|
|
$hash_match, |
474
|
|
|
|
|
|
|
$file_mtime); |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# Write to the cache and tick various counters. |
478
|
|
|
|
|
|
|
sub __to_cache { |
479
|
36
|
|
|
36
|
|
95
|
my ($self, $cache_filename, $hash_current, $file_id, $file_mtime) = @_; |
480
|
|
|
|
|
|
|
|
481
|
36
|
|
|
|
|
978
|
my $had_cache = -f $cache_filename; |
482
|
36
|
50
|
|
|
|
121
|
if ($self->__write_cachefile($cache_filename, $hash_current, |
483
|
|
|
|
|
|
|
$file_id, $file_mtime)) { |
484
|
36
|
100
|
|
|
|
124
|
if ($self->{opt}{cache_verbose}) { |
485
|
20
|
|
|
|
|
67
|
print "$Sub: writing $cache_filename SUCCESS\n"; |
486
|
|
|
|
|
|
|
} |
487
|
36
|
|
|
|
|
89
|
$self->{__cachewrites}++; |
488
|
36
|
100
|
|
|
|
185
|
unless ($had_cache) { |
489
|
35
|
|
|
|
|
226
|
$self->{__cachecreates}++; |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
} else { |
492
|
0
|
0
|
|
|
|
0
|
if ($self->{opt}{cache_verbose}) { |
493
|
0
|
|
|
|
|
0
|
print "$Sub: writing $cache_filename FAILURE\n"; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# Import the fields we care about from the cached data. |
499
|
|
|
|
|
|
|
sub __import_cached { |
500
|
20
|
|
|
20
|
|
38
|
my ($self, $file_id, $cached) = @_; |
501
|
|
|
|
|
|
|
|
502
|
20
|
|
|
|
|
32
|
for my $k (@CACHE_FIELDS) { |
503
|
140
|
|
|
|
|
334
|
$self->{$k}{$file_id} = $cached->{$k}; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
20
|
|
|
|
|
33
|
return 1; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# Clear the cached fields. Used especially in preparation of import. |
510
|
|
|
|
|
|
|
sub __clear_cached { |
511
|
186
|
|
|
186
|
|
349
|
my ($self, $file_id) = @_; |
512
|
|
|
|
|
|
|
|
513
|
186
|
|
|
|
|
393
|
for my $k (@CACHE_FIELDS_CLEAR) { |
514
|
930
|
|
|
|
|
2168
|
delete $self->{$k}{$file_id}; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
186
|
|
|
|
|
338
|
return 1; |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub __parse_error { |
521
|
5
|
|
|
5
|
|
15
|
my ($self, $file_id, $file, $fileloc, $error) = @_; |
522
|
5
|
50
|
|
|
|
20
|
if (defined $fileloc) { |
523
|
5
|
|
|
|
|
25
|
warn qq[$Sub: $error in $fileloc\n]; |
524
|
|
|
|
|
|
|
} else { |
525
|
0
|
|
|
|
|
0
|
warn qq[$Sub: $error\n]; |
526
|
|
|
|
|
|
|
} |
527
|
5
|
|
33
|
|
|
369
|
$self->{file_parse_errors}{$file_id}{$fileloc // $file} = $error; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
sub __doc_create { |
531
|
177
|
|
|
177
|
|
372
|
my ($self, $arg, $file, $file_id) = @_; |
532
|
177
|
|
|
|
|
254
|
my $doc; |
533
|
177
|
|
|
|
|
269
|
eval { $doc = PPI::Document->new($arg) }; |
|
177
|
|
|
|
|
1193
|
|
534
|
177
|
100
|
|
|
|
4884197
|
unless (defined $doc) { |
535
|
1
|
|
|
|
|
5
|
$self->__parse_error($file_id, $file, $file, |
536
|
|
|
|
|
|
|
"PPI::Document creation failed"); |
537
|
|
|
|
|
|
|
} else { |
538
|
176
|
|
|
|
|
275
|
my $complete; |
539
|
176
|
|
|
|
|
269
|
eval { $complete = $doc->complete }; |
|
176
|
|
|
|
|
680
|
|
540
|
176
|
100
|
|
|
|
568272
|
unless ($complete) { |
541
|
4
|
|
|
|
|
9
|
my $pseudo = $file eq '-'; |
542
|
4
|
50
|
33
|
|
|
45
|
if (!$pseudo && ! -f $file) { |
|
|
50
|
33
|
|
|
|
|
543
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, $file, undef, |
544
|
|
|
|
|
|
|
"Missing file"); |
545
|
|
|
|
|
|
|
} elsif (!$pseudo && ! -s $file) { |
546
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, $file, undef, |
547
|
|
|
|
|
|
|
"Empty file"); |
548
|
|
|
|
|
|
|
} else { |
549
|
4
|
|
|
|
|
14
|
$self->__parse_error($file_id, $file, $file, |
550
|
|
|
|
|
|
|
"PPI::Document incomplete"); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} |
554
|
177
|
|
|
|
|
444
|
return $doc; |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# Process a given filename. |
558
|
|
|
|
|
|
|
sub __process_file { |
559
|
288
|
|
|
288
|
|
583
|
my ($self, $arg, $file, $process_depth) = @_; |
560
|
288
|
|
66
|
|
|
1076
|
$file //= $arg; |
561
|
288
|
|
|
|
|
714
|
$self->{file_counts}{$file}++; |
562
|
288
|
100
|
|
|
|
1500
|
if ($file eq '-') { # Pseudofile. |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
563
|
10
|
50
|
|
|
|
38
|
if ($self->{opt}{process_verbose}) { |
564
|
0
|
|
|
|
|
0
|
printf "$Sub: %*s%s\n", $process_depth + 1, ' ', $file; |
565
|
|
|
|
|
|
|
} |
566
|
10
|
|
|
|
|
38
|
my $file_id = $self->__assign_file_id($file); |
567
|
10
|
|
|
|
|
42
|
my $doc = $self->__doc_create($arg, $file, $file_id); |
568
|
10
|
50
|
|
|
|
37
|
return unless defined $doc; |
569
|
10
|
|
|
|
|
33
|
$self->{__docscreated}++; |
570
|
10
|
|
|
|
|
39
|
$self->__process_id($doc, $file_id, $process_depth); |
571
|
|
|
|
|
|
|
} elsif ($self->{seen_file}{$file}) { |
572
|
91
|
50
|
66
|
|
|
347
|
if ($self->{opt}{process_verbose} && $self->{opt}{recurse_verbose}) { |
573
|
0
|
|
|
|
|
0
|
printf "$Sub: %*s%s [seen]\n", $process_depth + 1, ' ', $file; |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
} elsif (! $self->{seen_file}{$file}++) { |
576
|
187
|
100
|
|
|
|
564
|
if ($self->{opt}{process_verbose}) { |
577
|
18
|
|
|
|
|
62
|
printf "$Sub: %*s%s\n", $process_depth + 1, ' ', $file; |
578
|
|
|
|
|
|
|
} |
579
|
187
|
|
|
|
|
549
|
my $file_id = $self->__assign_file_id($file); |
580
|
187
|
|
|
|
|
598
|
my ($cache_filename, $cached, $hash_current, |
581
|
|
|
|
|
|
|
$hash_match, $file_mtime) = |
582
|
|
|
|
|
|
|
$self->__check_cached($file); |
583
|
187
|
100
|
|
|
|
484
|
if ($hash_match) { |
584
|
20
|
|
|
|
|
52
|
$self->__clear_cached($file_id); |
585
|
20
|
|
|
|
|
50
|
$self->__import_cached($file_id, $cached); |
586
|
20
|
|
|
|
|
71
|
$self->__process_cached_incs($file_id, $process_depth); |
587
|
20
|
|
|
|
|
37
|
$self->{__cachereads}++; |
588
|
|
|
|
|
|
|
} else { |
589
|
167
|
|
|
|
|
507
|
my $doc = $self->__doc_create($arg, $file, $file_id); |
590
|
167
|
100
|
|
|
|
446
|
return unless defined $doc; |
591
|
166
|
|
|
|
|
557
|
$self->__clear_cached($file_id); |
592
|
166
|
|
|
|
|
1204
|
$self->__process_id($doc, $file_id, $process_depth); |
593
|
166
|
|
|
|
|
738
|
$self->{__docscreated}++; |
594
|
|
|
|
|
|
|
} |
595
|
186
|
100
|
66
|
|
|
335638
|
if (defined $cache_filename && |
|
|
|
100
|
|
|
|
|
596
|
|
|
|
|
|
|
defined $hash_current && |
597
|
|
|
|
|
|
|
!$hash_match) { |
598
|
36
|
100
|
|
|
|
132
|
if ($self->__to_cache($cache_filename, $hash_current, |
599
|
|
|
|
|
|
|
$file_id, $file_mtime)) { |
600
|
30
|
100
|
66
|
|
|
200
|
if (!$hash_match && defined $cached) { |
601
|
1
|
|
|
|
|
6
|
$self->{__cacheupdates}++; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
} |
606
|
287
|
|
|
|
|
1572
|
return $self->{file_id}{$file}; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# Counter getters. |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub docs_created { |
612
|
10
|
|
|
10
|
1
|
24
|
my ($self) = @_; |
613
|
10
|
|
100
|
|
|
120
|
return $self->{__docscreated} // 0; |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
sub cache_reads { |
617
|
9
|
|
|
9
|
1
|
18
|
my ($self) = @_; |
618
|
9
|
|
100
|
|
|
75
|
return $self->{__cachereads} // 0; |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
sub cache_writes { |
622
|
12
|
|
|
12
|
1
|
24
|
my ($self) = @_; |
623
|
12
|
|
100
|
|
|
86
|
return $self->{__cachewrites} // 0; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
sub cache_creates { |
627
|
6
|
|
|
6
|
0
|
13
|
my ($self) = @_; |
628
|
6
|
|
100
|
|
|
51
|
return $self->{__cachecreates} // 0; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
sub cache_updates { |
632
|
6
|
|
|
6
|
0
|
16
|
my ($self) = @_; |
633
|
6
|
|
100
|
|
|
43
|
return $self->{__cacheupdates} // 0; |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
sub cache_deletes { |
637
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
638
|
0
|
|
0
|
|
|
0
|
return $self->{__cachedeletes} // 0; |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# For results imported from cache, process any cached inclusions. |
642
|
|
|
|
|
|
|
# The [6] is the include file, the [7] will become its (new) file id. |
643
|
|
|
|
|
|
|
sub __process_cached_incs { |
644
|
20
|
|
|
20
|
|
35
|
my ($self, $file_id, $process_depth) = @_; |
645
|
20
|
|
|
|
|
26
|
for my $inc (@{ $self->{file_incs}{$file_id} }) { |
|
20
|
|
|
|
|
60
|
|
646
|
31
|
|
|
|
|
48
|
my $include_file = $inc->[6]; |
647
|
31
|
|
|
|
|
82
|
$self->__process_file($include_file, undef, |
648
|
|
|
|
|
|
|
$process_depth + 1); |
649
|
31
|
|
|
|
|
79
|
$inc->[7] = $self->{file_id}{$include_file}; |
650
|
|
|
|
|
|
|
} |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
# For freshly computed results, process any cached inclusions. |
654
|
|
|
|
|
|
|
# The [6] is the include file, the [7] will become its (new) file id. |
655
|
|
|
|
|
|
|
sub __process_pending_incs { |
656
|
176
|
|
|
176
|
|
404
|
my ($self, $file_id, $process_depth) = @_; |
657
|
176
|
50
|
|
|
|
559
|
if ($self->{__incs_flush}{$file_id}) { |
658
|
|
|
|
|
|
|
$self->{file_incs}{$file_id} = |
659
|
176
|
|
|
|
|
490
|
delete $self->{__incs_pending}{$file_id}; |
660
|
176
|
|
|
|
|
248
|
for my $inc (@{ $self->{file_incs}{$file_id} }) { |
|
176
|
|
|
|
|
497
|
|
661
|
215
|
|
|
|
|
452
|
my $include_file = $inc->[6]; |
662
|
215
|
|
|
|
|
1048
|
$self->__process_file($include_file, undef, |
663
|
|
|
|
|
|
|
$process_depth + 1); |
664
|
215
|
|
|
|
|
712
|
$inc->[7] = $self->{file_id}{$include_file}; |
665
|
|
|
|
|
|
|
} |
666
|
176
|
|
|
|
|
849
|
delete $self->{__incs_flush}{$file_id}; # Defuse the trigger. |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
# Process a given PPI document, that has a given file id. |
671
|
|
|
|
|
|
|
sub __process_id { |
672
|
176
|
|
|
176
|
|
409
|
my ($self, $doc, $file_id, $process_depth) = @_; |
673
|
176
|
|
|
|
|
251
|
my @queue = @{$doc->{children}}; |
|
176
|
|
|
|
|
670
|
|
674
|
176
|
|
|
|
|
282
|
my $scope_depth = 0; |
675
|
176
|
|
|
|
|
603
|
my %package = ( 0 => 'main' ); |
676
|
176
|
|
|
|
|
395
|
my $package = $package{$scope_depth}; |
677
|
176
|
|
|
|
|
246
|
my $prev_package; |
678
|
|
|
|
|
|
|
my $elem; |
679
|
0
|
|
|
|
|
0
|
my $prev_elem; |
680
|
176
|
|
|
|
|
346
|
my $filename = $FILE_BY_ID{$file_id}; |
681
|
176
|
|
|
|
|
231
|
my $fileloc; |
682
|
176
|
|
|
|
|
504
|
while (@queue) { |
683
|
9812
|
|
|
|
|
13010
|
$elem = shift @queue; |
684
|
9812
|
|
|
|
|
25801
|
my $linenumber = $elem->line_number; |
685
|
9812
|
|
|
|
|
1212186
|
$fileloc = "$filename:$linenumber"; |
686
|
9812
|
|
|
|
|
10025
|
if (0) { |
687
|
|
|
|
|
|
|
printf("$fileloc elem = %s[%s]\n", |
688
|
|
|
|
|
|
|
$filename, ref $elem, $elem->content); |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
my @children = exists $elem->{children} ? |
691
|
9812
|
100
|
|
|
|
22030
|
__normalize_whitespace(@{$elem->{children}}) : (); |
|
2230
|
|
|
|
|
5692
|
|
692
|
9812
|
100
|
|
|
|
39219
|
if ($elem->isa('PPI::Token::Structure')) { |
693
|
|
|
|
|
|
|
# { ... } |
694
|
1583
|
100
|
|
|
|
3876
|
if ($elem->content eq '{') { |
|
|
100
|
|
|
|
|
|
695
|
558
|
|
|
|
|
2308
|
$scope_depth++; |
696
|
|
|
|
|
|
|
} elsif ($elem->content eq '}') { |
697
|
558
|
50
|
|
|
|
4491
|
if ($scope_depth <= 0) { |
698
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, $filename, $fileloc, |
699
|
|
|
|
|
|
|
"scope pop underflow"); |
700
|
|
|
|
|
|
|
} else { |
701
|
558
|
|
|
|
|
691
|
$scope_depth--; |
702
|
558
|
|
|
|
|
1233
|
delete @package{ grep { $_ > $scope_depth } keys %package }; |
|
661
|
|
|
|
|
2057
|
|
703
|
32
|
|
|
32
|
|
191
|
use List::Util qw[first]; |
|
32
|
|
|
|
|
62
|
|
|
32
|
|
|
|
|
52411
|
|
704
|
|
|
|
|
|
|
$package = |
705
|
|
|
|
|
|
|
$package{$scope_depth} // |
706
|
558
|
|
66
|
102
|
|
2220
|
first { defined } @package{reverse 0..$scope_depth}; |
|
102
|
|
|
|
|
288
|
|
707
|
558
|
100
|
66
|
|
|
2803
|
if (defined $prev_package && $package ne $prev_package) { |
708
|
26
|
|
|
|
|
52
|
if (0) { |
709
|
|
|
|
|
|
|
print "$fileloc: package change: $prev_package -> $package\n"; |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
$self->__close_open_package( |
712
|
26
|
|
|
|
|
85
|
$file_id, $prev_package, $prev_elem, |
713
|
|
|
|
|
|
|
$package, $elem); |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
} |
718
|
9812
|
100
|
|
|
|
24282
|
if (@children) { |
719
|
1922
|
100
|
66
|
|
|
18108
|
if ($elem->isa('PPI::Statement::Package') && @children >= 2) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
720
|
|
|
|
|
|
|
# package ... |
721
|
|
|
|
|
|
|
# |
722
|
|
|
|
|
|
|
# Remember to test 'use mro' and look for next::can(). |
723
|
247
|
|
|
|
|
778
|
$package = $children[2]->content; |
724
|
247
|
|
|
|
|
1116
|
$package{$scope_depth} = $package; |
725
|
247
|
50
|
33
|
|
|
1139
|
if (defined $package && length $package) { |
726
|
|
|
|
|
|
|
# Okay, keep going. |
727
|
|
|
|
|
|
|
} else { |
728
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, |
729
|
|
|
|
|
|
|
$filename, |
730
|
|
|
|
|
|
|
$fileloc, "missing package"); |
731
|
|
|
|
|
|
|
} |
732
|
247
|
100
|
|
|
|
482
|
if (defined $prev_package) { |
733
|
130
|
50
|
|
|
|
436
|
if ($package ne $prev_package) { |
734
|
130
|
|
|
|
|
143
|
if (0) { |
735
|
|
|
|
|
|
|
print "$fileloc: package change: $prev_package -> $package\n"; |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
$self->__close_open_package( |
738
|
130
|
|
|
|
|
377
|
$file_id, $prev_package, $prev_elem, |
739
|
|
|
|
|
|
|
$package, $elem); |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
} else { |
742
|
117
|
|
|
|
|
135
|
if (0) { |
743
|
|
|
|
|
|
|
print "$fileloc: package first: $package\n"; |
744
|
|
|
|
|
|
|
} |
745
|
117
|
|
|
|
|
408
|
$self->__open_package($file_id, $package, $elem); |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
} elsif ($elem->isa('PPI::Statement::Sub') && |
748
|
|
|
|
|
|
|
defined $elem->name && |
749
|
|
|
|
|
|
|
!$elem->forward # Not a forward declaration. |
750
|
|
|
|
|
|
|
) { |
751
|
|
|
|
|
|
|
# sub ... |
752
|
457
|
|
|
|
|
21462
|
my $sub = $elem->name; |
753
|
457
|
100
|
|
|
|
10259
|
unless ($sub =~ /::/) { # sub x::y::z { ... } |
754
|
455
|
|
50
|
|
|
943
|
$package //= 'main'; |
755
|
455
|
|
|
|
|
955
|
$sub = $package . '::' . $sub; |
756
|
|
|
|
|
|
|
} |
757
|
457
|
|
|
|
|
1168
|
my $finish = $elem->block->finish; |
758
|
457
|
50
|
|
|
|
8492
|
unless (defined $finish) { |
759
|
|
|
|
|
|
|
# E.g. Devel::Peek:debug_flags() fails to have a finish. |
760
|
0
|
|
|
|
|
0
|
$finish = $elem; # Fake it. |
761
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, |
762
|
|
|
|
|
|
|
$filename, |
763
|
|
|
|
|
|
|
$fileloc, "missing finish"); |
764
|
|
|
|
|
|
|
} |
765
|
457
|
|
|
|
|
538
|
push @{ $self->{file_subs}{$file_id} }, |
|
457
|
|
|
|
|
1732
|
|
766
|
|
|
|
|
|
|
[ |
767
|
|
|
|
|
|
|
$sub, # 0 |
768
|
|
|
|
|
|
|
$elem->line_number, # 1 |
769
|
|
|
|
|
|
|
$elem->column_number, # 2 |
770
|
|
|
|
|
|
|
$finish->line_number, # 3 |
771
|
|
|
|
|
|
|
$finish->column_number, # 4 |
772
|
|
|
|
|
|
|
]; |
773
|
|
|
|
|
|
|
} elsif ($elem->isa('PPI::Statement')) { |
774
|
|
|
|
|
|
|
# use, no, require |
775
|
968
|
|
|
|
|
2734
|
my $stmt_content = $children[0]->content; |
776
|
968
|
|
|
|
|
34267
|
my $include = $children[2]; |
777
|
968
|
100
|
|
|
|
2258
|
next unless defined $include; |
778
|
851
|
|
|
|
|
2214
|
my $include_content = $include->content; |
779
|
851
|
|
|
|
|
8813
|
my $including_module; |
780
|
|
|
|
|
|
|
my $including_file; |
781
|
851
|
100
|
66
|
|
|
8038
|
if ($elem->isa('PPI::Statement::Include') && |
|
|
100
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
782
|
|
|
|
|
|
|
# use/no/require Module |
783
|
|
|
|
|
|
|
$stmt_content =~ /^(?:use|no|require)$/ && |
784
|
|
|
|
|
|
|
$include->isa('PPI::Token::Word') && |
785
|
|
|
|
|
|
|
$include_content !~ /^v?5/) { |
786
|
173
|
|
|
|
|
262
|
$including_module = 1; |
787
|
|
|
|
|
|
|
} elsif ($stmt_content =~ /^(?:require|do)$/ && |
788
|
|
|
|
|
|
|
$include->isa('PPI::Token::Quote')) { |
789
|
|
|
|
|
|
|
# require/do "file" |
790
|
50
|
|
|
|
|
92
|
$including_file = 1; |
791
|
|
|
|
|
|
|
} else { |
792
|
|
|
|
|
|
|
# Not a use/no/require/do, quietly exit stage left. |
793
|
628
|
|
|
|
|
2298
|
next; |
794
|
|
|
|
|
|
|
} |
795
|
223
|
50
|
|
|
|
506
|
unless (defined $include) { |
796
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, $fileloc, "missing include"); |
797
|
0
|
|
|
|
|
0
|
next; |
798
|
|
|
|
|
|
|
} |
799
|
223
|
|
|
|
|
318
|
my $last = $children[-1]; |
800
|
223
|
|
|
|
|
301
|
my $include_file; |
801
|
|
|
|
|
|
|
my $include_string; |
802
|
223
|
100
|
|
|
|
496
|
if ($including_module) { |
|
|
50
|
|
|
|
|
|
803
|
173
|
|
|
|
|
308
|
$include_string = $include_content; |
804
|
173
|
|
|
|
|
488
|
$include_file = $self->__find_module($include_content); |
805
|
173
|
|
|
|
|
543
|
$self->{file_modules}{$file_id}{$include_content}++; |
806
|
173
|
100
|
|
|
|
530
|
unless (defined $include_file) { |
807
|
5
|
|
|
|
|
14
|
$self->{file_missing_modules}{$file_id}{$include_content}{$fileloc}++; |
808
|
5
|
|
|
|
|
18
|
warn "$Sub: warning: Failed to find module '$include_string' in $fileloc\n"; |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
} elsif ($including_file) { |
811
|
50
|
|
|
|
|
231
|
$include_string = $include->string; |
812
|
50
|
|
|
|
|
394
|
$include_file = $self->__find_file($include_string); |
813
|
50
|
50
|
|
|
|
166
|
unless (defined $include_file) { |
814
|
0
|
|
|
|
|
0
|
warn "$Sub: warning: Failed to find file '$include_string' in $fileloc\n"; |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} |
817
|
223
|
100
|
|
|
|
688
|
if (defined $include_file) { |
818
|
218
|
100
|
|
|
|
580
|
if ($self->{opt}{recurse}) { |
819
|
215
|
|
|
|
|
268
|
push @{ $self->{__incs_pending}{$file_id} }, |
|
215
|
|
|
|
|
932
|
|
820
|
|
|
|
|
|
|
[ |
821
|
|
|
|
|
|
|
$stmt_content, # 0 |
822
|
|
|
|
|
|
|
$elem->line_number, # 1 |
823
|
|
|
|
|
|
|
$elem->column_number, # 2 |
824
|
|
|
|
|
|
|
$last->line_number, # 3 |
825
|
|
|
|
|
|
|
$last->column_number, # 4 |
826
|
|
|
|
|
|
|
$include_string, # 5 |
827
|
|
|
|
|
|
|
$include_file, # 6 |
828
|
|
|
|
|
|
|
# 7 will be the file_id of include_file |
829
|
|
|
|
|
|
|
]; |
830
|
|
|
|
|
|
|
} else { |
831
|
3
|
|
|
|
|
7
|
$self->__assign_file_id($include_file); |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
} |
835
|
|
|
|
|
|
|
} |
836
|
9067
|
100
|
|
|
|
74025
|
if ($elem->isa('PPI::Structure')) { |
837
|
|
|
|
|
|
|
# { ... } |
838
|
558
|
50
|
|
|
|
1506
|
unshift @queue, $elem->finish if $elem->finish; |
839
|
558
|
|
|
|
|
4703
|
unshift @queue, @children; |
840
|
558
|
50
|
|
|
|
1380
|
unshift @queue, $elem->start if $elem->start; |
841
|
|
|
|
|
|
|
} else { |
842
|
8509
|
|
|
|
|
11662
|
unshift @queue, @children; |
843
|
|
|
|
|
|
|
} |
844
|
9067
|
|
|
|
|
14237
|
$prev_elem = $elem; |
845
|
9067
|
|
|
|
|
23288
|
$prev_package = $package; |
846
|
|
|
|
|
|
|
} |
847
|
176
|
50
|
|
|
|
476
|
if (defined $elem) { |
|
|
0
|
|
|
|
|
|
848
|
176
|
|
|
|
|
556
|
$self->{file_lines}{$file_id} = $elem->line_number; |
849
|
176
|
|
|
|
|
2562
|
$self->__close_package($file_id, $package, $elem); |
850
|
0
|
|
|
|
|
0
|
} elsif (@{ $doc->{children} }) { |
851
|
0
|
|
|
|
|
0
|
$self->__parse_error($file_id, $filename, |
852
|
|
|
|
|
|
|
"Undefined token when leaving"); |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
# Mark the __incs_pending as ready to be recursed into. |
856
|
176
|
|
|
|
|
3159
|
$self->{__incs_flush}{$file_id}++; |
857
|
|
|
|
|
|
|
|
858
|
176
|
|
|
|
|
658
|
$self->__process_pending_incs($file_id, $process_depth); |
859
|
|
|
|
|
|
|
} |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
sub __trash_cache { |
862
|
43
|
|
|
43
|
|
81
|
my $self = shift; |
863
|
43
|
|
|
|
|
147
|
delete $self->{result_cache}; |
864
|
43
|
|
|
|
|
104
|
delete $self->{seen_file}; |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
# Parse the given filenames (or if a scalar ref, a string of code, |
868
|
|
|
|
|
|
|
# in which case filename is assumed to be '-'). |
869
|
|
|
|
|
|
|
sub process { |
870
|
43
|
|
|
43
|
0
|
1416
|
my $self = shift; |
871
|
43
|
|
|
|
|
181
|
$self->__trash_cache; |
872
|
43
|
|
|
|
|
72
|
my $success = 1; |
873
|
43
|
|
|
|
|
122
|
for my $arg (@_) { |
874
|
43
|
|
|
|
|
67
|
my $file; |
875
|
43
|
|
|
|
|
95
|
my $ref = ref $arg; |
876
|
43
|
100
|
|
|
|
156
|
if ($ref eq '') { |
|
|
100
|
|
|
|
|
|
877
|
32
|
|
|
|
|
68
|
$file = $arg; |
878
|
|
|
|
|
|
|
} elsif ($ref eq 'SCALAR') { |
879
|
10
|
|
|
|
|
23
|
$file = '-'; |
880
|
|
|
|
|
|
|
} else { |
881
|
1
|
|
|
|
|
7
|
warn "$Sub: Unexpected argument '$arg' (ref: $ref)\n"; |
882
|
1
|
|
|
|
|
163
|
$success = 0; |
883
|
1
|
|
|
|
|
4
|
next; |
884
|
|
|
|
|
|
|
} |
885
|
42
|
|
|
|
|
190
|
my $file_id = $self->__process_file($arg, $file, 0); |
886
|
42
|
100
|
|
|
|
179
|
unless (defined $file_id) { |
887
|
1
|
|
|
|
|
2
|
$success = 0; |
888
|
1
|
|
|
|
|
4
|
next; |
889
|
|
|
|
|
|
|
} |
890
|
41
|
|
|
|
|
243
|
$self->{__process}{ $file_id }++; |
891
|
|
|
|
|
|
|
} |
892
|
43
|
|
|
|
|
289
|
return $success; |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
sub process_files_from_cache { |
896
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
897
|
0
|
|
|
|
|
0
|
my %files; |
898
|
0
|
|
|
|
|
0
|
$self->find_cache_files(\%files); |
899
|
0
|
0
|
0
|
|
|
0
|
if ($self->{opt}{process_verbose} || $self->{opt}{cache_verbose}) { |
900
|
0
|
|
|
|
|
0
|
my $cache_directory = $self->{opt}{cache_directory}; |
901
|
0
|
|
|
|
|
0
|
printf("$Sub: found %d cache files from %s\n", |
902
|
|
|
|
|
|
|
scalar keys %files, $cache_directory); |
903
|
|
|
|
|
|
|
} |
904
|
0
|
|
|
|
|
0
|
$self->process(sort keys %files); |
905
|
|
|
|
|
|
|
} |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
sub process_files_from_system { |
908
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
909
|
0
|
|
|
|
|
0
|
my %files; |
910
|
0
|
|
|
|
|
0
|
$self->find_system_files(\%files); |
911
|
0
|
0
|
|
|
|
0
|
if ($self->{opt}{process_verbose}) { |
912
|
0
|
|
|
|
|
0
|
my $cache_directory = $self->{opt}{cache_directory}; |
913
|
0
|
|
|
|
|
0
|
printf("$Sub: found %d system files from @INC\n", |
914
|
|
|
|
|
|
|
scalar keys %files); |
915
|
|
|
|
|
|
|
} |
916
|
0
|
|
|
|
|
0
|
$self->process(sort keys %files); |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
# Returns the seen filenames. |
920
|
|
|
|
|
|
|
sub files { |
921
|
8
|
|
|
8
|
1
|
18
|
my $self = shift; |
922
|
8
|
50
|
|
|
|
43
|
unless (defined $self->{result_cache}{files}) { |
923
|
8
|
50
|
|
|
|
32
|
return unless $self->{file_id}; |
924
|
8
|
|
|
|
|
18
|
$self->{result_cache}{files} = [ sort keys %{ $self->{file_id} } ]; |
|
8
|
|
|
|
|
120
|
|
925
|
|
|
|
|
|
|
} |
926
|
8
|
|
|
|
|
26
|
return @{ $self->{result_cache}{files} }; |
|
8
|
|
|
|
|
62
|
|
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
# Computes the total number of lines. |
930
|
|
|
|
|
|
|
sub total_lines { |
931
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
932
|
1
|
50
|
|
|
|
6
|
unless (defined $self->{result_cache}{total_lines}) { |
933
|
1
|
50
|
|
|
|
4
|
return unless $self->{file_lines}; |
934
|
32
|
|
|
32
|
|
193
|
use List::Util qw[sum]; |
|
32
|
|
|
|
|
71
|
|
|
32
|
|
|
|
|
61777
|
|
935
|
1
|
|
|
|
|
2
|
$self->{result_cache}{total_lines} = sum grep { defined } values %{ $self->{file_lines} }; |
|
6
|
|
|
|
|
24
|
|
|
1
|
|
|
|
|
4
|
|
936
|
|
|
|
|
|
|
} |
937
|
1
|
|
|
|
|
7
|
return $self->{result_cache}{total_lines}; |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# Lines in a file. |
941
|
|
|
|
|
|
|
sub file_lines { |
942
|
6
|
|
|
6
|
1
|
27
|
my ($self, $file) = @_; |
943
|
6
|
|
|
|
|
26
|
return $self->{file_lines}{$self->{file_id}{$file}}; |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
# Returns the known file ids. |
947
|
|
|
|
|
|
|
sub __file_ids { |
948
|
46
|
|
|
46
|
|
77
|
my $self = shift; |
949
|
46
|
100
|
|
|
|
169
|
unless (defined $self->{result_cache}{__file_ids}) { |
950
|
30
|
50
|
|
|
|
96
|
return unless $FILE_ID; |
951
|
30
|
|
|
|
|
159
|
$self->{result_cache}{__file_ids} = [ 0..$FILE_ID-1 ]; |
952
|
|
|
|
|
|
|
} |
953
|
46
|
|
|
|
|
80
|
return @{ $self->{result_cache}{__file_ids} }; |
|
46
|
|
|
|
|
155
|
|
954
|
|
|
|
|
|
|
} |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
# Returns the reference count of a filename. |
957
|
|
|
|
|
|
|
sub file_count { |
958
|
6
|
|
|
6
|
1
|
26
|
my ($self, $file) = @_; |
959
|
6
|
50
|
|
|
|
16
|
return unless $self->{file_counts}; |
960
|
6
|
|
|
|
|
21
|
return $self->{file_counts}->{$file}; |
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# Computes the seen modules. |
964
|
|
|
|
|
|
|
sub __modules { |
965
|
12
|
|
|
12
|
|
19
|
my $self = shift; |
966
|
12
|
100
|
|
|
|
43
|
unless (defined $self->{result_cache}{modules}) { |
967
|
8
|
50
|
|
|
|
28
|
return unless $self->{file_modules}; |
968
|
8
|
|
|
|
|
15
|
delete $self->{modules}; |
969
|
8
|
|
|
|
|
25
|
for my $f ($self->__file_ids) { |
970
|
100
|
|
|
|
|
112
|
for my $m (keys %{ $self->{file_modules}{$f} }) { |
|
100
|
|
|
|
|
341
|
|
971
|
63
|
|
|
|
|
167
|
$self->{modules}{$m} += $self->{file_modules}{$f}{$m}; |
972
|
|
|
|
|
|
|
} |
973
|
|
|
|
|
|
|
} |
974
|
8
|
|
|
|
|
105
|
$self->{result_cache}{modules} = [ sort keys %{ $self->{modules} } ]; |
|
8
|
|
|
|
|
58
|
|
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
} |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# Returns the seen modules. |
979
|
|
|
|
|
|
|
sub modules { |
980
|
9
|
|
|
9
|
1
|
19
|
my $self = shift; |
981
|
9
|
|
|
|
|
30
|
$self->__modules; |
982
|
9
|
|
|
|
|
17
|
return @{ $self->{result_cache}{modules} }; |
|
9
|
|
|
|
|
54
|
|
983
|
|
|
|
|
|
|
} |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
# Computes the missing modules. |
986
|
|
|
|
|
|
|
sub __missing_modules { |
987
|
9
|
|
|
9
|
|
12
|
my $self = shift; |
988
|
9
|
100
|
|
|
|
30
|
unless (defined $self->{result_cache}{missing_modules}) { |
989
|
4
|
|
50
|
|
|
21
|
$self->{result_cache}{missing_modules} //= []; |
990
|
4
|
|
50
|
|
|
18
|
$self->{result_cache}{missing_modules_files} //= {}; |
991
|
4
|
|
50
|
|
|
18
|
$self->{result_cache}{missing_modules_count} //= {}; |
992
|
4
|
100
|
|
|
|
10
|
return unless $self->{file_missing_modules}; |
993
|
3
|
|
|
|
|
5
|
delete $self->{missing_modules}; |
994
|
3
|
|
|
|
|
9
|
delete $self->{missing_modules_files}; |
995
|
3
|
|
|
|
|
6
|
delete $self->{missing_modules_lines}; |
996
|
3
|
|
|
|
|
6
|
delete $self->{missing_modules_count}; |
997
|
3
|
|
|
|
|
9
|
for my $f ($self->__file_ids) { |
998
|
3
|
|
|
|
|
7
|
my $file = $FILE_BY_ID{$f}; |
999
|
3
|
|
|
|
|
5
|
for my $m (keys %{ $self->{file_missing_modules}{$f} }) { |
|
3
|
|
|
|
|
10
|
|
1000
|
5
|
|
|
|
|
4
|
for my $l (keys %{ $self->{file_missing_modules}{$f}{$m} }) { |
|
5
|
|
|
|
|
14
|
|
1001
|
5
|
|
|
|
|
8
|
my $c = $self->{file_missing_modules}{$f}{$m}{$l}; |
1002
|
5
|
|
|
|
|
12
|
$self->{missing_modules_files}{$m}{$file} += $c; |
1003
|
5
|
|
|
|
|
10
|
$self->{missing_modules_lines}{$m}{$l} += $c; |
1004
|
5
|
|
|
|
|
16
|
$self->{missing_modules_count}{$m} += $c; |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
} |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
$self->{result_cache}{missing_modules} = |
1009
|
3
|
|
|
|
|
6
|
[ sort keys %{ $self->{missing_modules_files} } ]; |
|
3
|
|
|
|
|
15
|
|
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# Returns the missing modules. |
1014
|
|
|
|
|
|
|
sub missing_modules { |
1015
|
3
|
|
|
3
|
1
|
8
|
my $self = shift; |
1016
|
3
|
|
|
|
|
9
|
$self->__missing_modules; |
1017
|
3
|
|
|
|
|
5
|
return @{ $self->{result_cache}{missing_modules} }; |
|
3
|
|
|
|
|
17
|
|
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# Returns the total reference count of a module name. |
1021
|
|
|
|
|
|
|
sub module_count { |
1022
|
3
|
|
|
3
|
1
|
14
|
my ($self, $module) = @_; |
1023
|
3
|
|
|
|
|
7
|
$self->__modules; |
1024
|
3
|
50
|
|
|
|
8
|
return 0 unless $self->{modules}; |
1025
|
3
|
|
50
|
|
|
14
|
return $self->{modules}->{$module} || 0; |
1026
|
|
|
|
|
|
|
} |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
# Returns the files referring a missing module. |
1029
|
|
|
|
|
|
|
sub missing_module_files { |
1030
|
1
|
|
|
1
|
1
|
3
|
my ($self, $module) = @_; |
1031
|
1
|
|
|
|
|
3
|
$self->__missing_modules; |
1032
|
1
|
50
|
|
|
|
4
|
return unless $self->{missing_modules_files}{$module}; |
1033
|
1
|
|
|
|
|
2
|
return sort keys %{ $self->{missing_modules_files}{$module} }; |
|
1
|
|
|
|
|
8
|
|
1034
|
|
|
|
|
|
|
} |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
# Returns the lines referring a missing module. |
1037
|
|
|
|
|
|
|
sub missing_module_lines { |
1038
|
1
|
|
|
1
|
1
|
3
|
my ($self, $module) = @_; |
1039
|
1
|
|
|
|
|
3
|
$self->__missing_modules; |
1040
|
1
|
50
|
|
|
|
5
|
return unless $self->{missing_modules_lines}{$module}; |
1041
|
1
|
|
|
|
|
7
|
return map { "$_->[0]:$_->[1]" } |
1042
|
0
|
0
|
|
|
|
0
|
sort { $a->[0] cmp $b->[0] || $a->[1] <=> $b->[1] } |
1043
|
1
|
50
|
|
|
|
10
|
map { /^(.+):(\d+)$/ ? [ $1, $2 ] : [ $_, 0 ] } |
1044
|
1
|
|
|
|
|
2
|
keys %{ $self->{missing_modules_lines}{$module} }; |
|
1
|
|
|
|
|
4
|
|
1045
|
|
|
|
|
|
|
} |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
# Returns the times a missing module was referred. |
1048
|
|
|
|
|
|
|
sub missing_module_count { |
1049
|
4
|
|
|
4
|
1
|
9
|
my ($self, $module) = @_; |
1050
|
4
|
|
|
|
|
12
|
$self->__missing_modules; |
1051
|
4
|
100
|
|
|
|
19
|
return 0 unless $self->{missing_modules_count}{$module}; |
1052
|
2
|
|
50
|
|
|
23
|
return $self->{missing_modules_count}{$module} || 0; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
# Computes the parse errors. |
1056
|
|
|
|
|
|
|
sub __parse_errors { |
1057
|
3
|
|
|
3
|
|
5
|
my $self = shift; |
1058
|
3
|
100
|
|
|
|
13
|
unless (defined $self->{result_cache}{parse_errors_files}) { |
1059
|
2
|
|
50
|
|
|
13
|
$self->{result_cache}{parse_errors_files} //= []; |
1060
|
2
|
100
|
|
|
|
6
|
return unless exists $self->{file_parse_errors}; |
1061
|
1
|
|
|
|
|
3
|
delete $self->{parse_errors_files}; |
1062
|
1
|
|
|
|
|
5
|
for my $f ($self->__file_ids) { |
1063
|
1
|
|
|
|
|
2
|
for my $l (keys %{ $self->{file_parse_errors}{$f} }) { |
|
1
|
|
|
|
|
4
|
|
1064
|
1
|
|
|
|
|
5
|
$self->{parse_errors_files}{$FILE_BY_ID{$f}}++; |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
} |
1067
|
|
|
|
|
|
|
$self->{result_cache}{parse_errors_files} = |
1068
|
1
|
|
|
|
|
2
|
[ sort keys %{ $self->{parse_errors_files} } ]; |
|
1
|
|
|
|
|
6
|
|
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
# Return the files with parse errors. |
1073
|
|
|
|
|
|
|
sub parse_errors_files { |
1074
|
2
|
|
|
2
|
1
|
13
|
my $self = shift; |
1075
|
2
|
|
|
|
|
6
|
$self->__parse_errors; |
1076
|
2
|
|
|
|
|
4
|
return @{ $self->{result_cache}{parse_errors_files} }; |
|
2
|
|
|
|
|
22
|
|
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
# Return the parse errors in a file, as a hash of filelocation -> error. |
1080
|
|
|
|
|
|
|
sub file_parse_errors { |
1081
|
1
|
|
|
1
|
1
|
4
|
my ($self, $file) = @_; |
1082
|
1
|
|
|
|
|
5
|
$self->__parse_errors; |
1083
|
1
|
50
|
|
|
|
4
|
return unless exists $self->{file_parse_errors}; |
1084
|
1
|
50
|
|
|
|
4
|
return unless defined $file; |
1085
|
1
|
|
|
|
|
2
|
my $file_id = $self->{file_id}{$file}; |
1086
|
1
|
50
|
|
|
|
3
|
return unless defined $file_id; |
1087
|
1
|
50
|
|
|
|
5
|
return unless exists $self->{file_parse_errors}{$file_id}; |
1088
|
1
|
|
|
|
|
2
|
return %{ $self->{file_parse_errors}{$file_id} }; |
|
1
|
|
|
|
|
7
|
|
1089
|
|
|
|
|
|
|
} |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
# Generates the subs or packages. |
1092
|
|
|
|
|
|
|
sub __subs_or_packages { |
1093
|
21
|
|
|
21
|
|
41
|
my ($self, $key, $cache) = @_; |
1094
|
21
|
100
|
|
|
|
92
|
unless (defined $self->{result_cache}{$cache}) { |
1095
|
19
|
50
|
|
|
|
61
|
return unless $self->{$key}; |
1096
|
19
|
|
|
|
|
32
|
my %uniq; |
1097
|
19
|
|
|
|
|
65
|
for my $f ($self->__file_ids) { |
1098
|
202
|
|
|
|
|
247
|
@uniq{ map { $_->[0] } @{ $self->{$key}{$f} } } = (); |
|
338
|
|
|
|
|
2488
|
|
|
202
|
|
|
|
|
628
|
|
1099
|
|
|
|
|
|
|
} |
1100
|
19
|
|
|
|
|
318
|
$self->{result_cache}{$cache} = [ sort keys %uniq ]; |
1101
|
|
|
|
|
|
|
} |
1102
|
21
|
|
|
|
|
51
|
return @{ $self->{result_cache}{$cache} }; |
|
21
|
|
|
|
|
243
|
|
1103
|
|
|
|
|
|
|
} |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
# Returns the subs. |
1106
|
|
|
|
|
|
|
sub subs { |
1107
|
12
|
|
|
12
|
1
|
25
|
my $self = shift; |
1108
|
12
|
|
|
|
|
45
|
return $self->__subs_or_packages(file_subs => 'subs'); |
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# Returns the packages. |
1112
|
|
|
|
|
|
|
sub packages { |
1113
|
9
|
|
|
9
|
1
|
17
|
my $self = shift; |
1114
|
9
|
|
|
|
|
32
|
return $self->__subs_or_packages(file_packages => 'packages'); |
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
# Generates the subs' or packages' files. |
1118
|
|
|
|
|
|
|
sub __subs_or_packages_and_files { |
1119
|
6
|
|
|
6
|
|
15
|
my ($self, $key, $cache) = @_; |
1120
|
6
|
50
|
|
|
|
33
|
unless (defined $self->{result_cache}{$cache}) { |
1121
|
6
|
50
|
|
|
|
26
|
return [] unless $self->{$key}; |
1122
|
6
|
|
|
|
|
10
|
my @cache; |
1123
|
6
|
|
|
|
|
29
|
for my $f ($self->__file_ids) { |
1124
|
36
|
|
|
|
|
46
|
for my $s (@{ $self->{$key}{$f} }) { |
|
36
|
|
|
|
|
103
|
|
1125
|
|
|
|
|
|
|
push @cache, |
1126
|
70
|
|
|
|
|
105
|
[ $s->[0], $f, @{$s}[1..$#$s] ]; |
|
70
|
|
|
|
|
228
|
|
1127
|
|
|
|
|
|
|
} |
1128
|
|
|
|
|
|
|
} |
1129
|
|
|
|
|
|
|
$self->{result_cache}{$cache} = [ |
1130
|
6
|
|
|
|
|
44
|
sort { $a->[0] cmp $b->[0] || |
1131
|
172
|
50
|
66
|
|
|
465
|
$FILE_BY_ID{$a->[1]} cmp $FILE_BY_ID{$b->[1]} || |
|
|
|
66
|
|
|
|
|
1132
|
|
|
|
|
|
|
$a->[2] <=> $b->[2] || |
1133
|
|
|
|
|
|
|
$a->[3] <=> $b->[3] } |
1134
|
|
|
|
|
|
|
@cache ]; |
1135
|
|
|
|
|
|
|
} |
1136
|
6
|
|
|
|
|
33
|
return $self->{result_cache}{$cache}; |
1137
|
|
|
|
|
|
|
} |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
# Returns the subs' files (an aref to iterate through). |
1140
|
|
|
|
|
|
|
sub __subs_files_full { |
1141
|
5
|
|
|
5
|
|
12
|
my $self = shift; |
1142
|
5
|
|
|
|
|
23
|
$self->__subs_or_packages_and_files(file_subs => '__subs_files'); |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
# Returns the packages' files (an aref to iterate through). |
1146
|
|
|
|
|
|
|
sub __packages_files_full { |
1147
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
1148
|
1
|
|
|
|
|
5
|
$self->__subs_or_packages_and_files(file_packages => '__packages_files'); |
1149
|
|
|
|
|
|
|
} |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
# Base class for returning iterator results. |
1152
|
|
|
|
|
|
|
package PPI::Xref::IterResultBase { |
1153
|
|
|
|
|
|
|
# Result as a string. |
1154
|
|
|
|
|
|
|
# The desired fields are selected, and concatenated. |
1155
|
|
|
|
|
|
|
sub string { |
1156
|
85
|
|
|
85
|
|
392
|
my $self = shift; |
1157
|
85
|
|
|
|
|
183
|
return $self->{cb}->($self->{it}); |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
# Result as an array. |
1160
|
|
|
|
|
|
|
sub array { |
1161
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1162
|
0
|
|
|
|
|
0
|
return @{ $self->{it} }; |
|
0
|
|
|
|
|
0
|
|
1163
|
|
|
|
|
|
|
} |
1164
|
|
|
|
|
|
|
} |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
package PPI::Xref::FileIterResult { |
1167
|
32
|
|
|
32
|
|
25304
|
use parent -norequire, 'PPI::Xref::IterResultBase'; |
|
32
|
|
|
|
|
9105
|
|
|
32
|
|
|
|
|
211
|
|
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
package PPI::Xref::FileIterBase { |
1171
|
|
|
|
|
|
|
# Base class method for stepping through the subs' and packages' files. |
1172
|
|
|
|
|
|
|
# Converts the file id into filename, and returns iterator result. |
1173
|
|
|
|
|
|
|
sub next { |
1174
|
|
|
|
|
|
|
# NOTE: while this is an iterator in the sense of returning the next |
1175
|
|
|
|
|
|
|
# result, this does not compute the next result because all the results |
1176
|
|
|
|
|
|
|
# have already been computed when the iterator was constructed. |
1177
|
76
|
|
|
76
|
|
2046
|
my $self = shift; |
1178
|
76
|
100
|
|
|
|
104
|
if ($self->{ix} < @{$self->{it}}) { |
|
76
|
|
|
|
|
206
|
|
1179
|
70
|
|
|
|
|
82
|
my @it = @{ $self->{it}->[$self->{ix}++] }; |
|
70
|
|
|
|
|
223
|
|
1180
|
70
|
|
|
|
|
131
|
$it[1] = $FILE_BY_ID{$it[1]}; |
1181
|
|
|
|
|
|
|
return bless { it => \@it, cb => $self->{cb} }, |
1182
|
70
|
|
|
|
|
277
|
'PPI::Xref::FileIterResult' |
1183
|
|
|
|
|
|
|
} |
1184
|
6
|
|
|
|
|
15
|
return; |
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
} |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
package PPI::Xref::SubsFilesIter { |
1189
|
32
|
|
|
32
|
|
5414
|
use parent -norequire, 'PPI::Xref::FileIterBase'; |
|
32
|
|
|
|
|
58
|
|
|
32
|
|
|
|
|
155
|
|
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
package PPI::Xref::PackagesFilesIter { |
1193
|
32
|
|
|
32
|
|
1685
|
use parent -norequire, 'PPI::Xref::FileIterBase'; |
|
32
|
|
|
|
|
52
|
|
|
32
|
|
|
|
|
143
|
|
1194
|
|
|
|
|
|
|
} |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
# Callback generator for subs' and packages' files. |
1197
|
|
|
|
|
|
|
# Selects the desired fields, and concatenates the fields. |
1198
|
|
|
|
|
|
|
sub __file_iter_callback { |
1199
|
6
|
|
|
6
|
|
11
|
my $opt = shift; |
1200
|
|
|
|
|
|
|
sub { |
1201
|
70
|
|
|
70
|
|
84
|
my $self = shift; |
1202
|
|
|
|
|
|
|
join($opt->{separator} // "\t", |
1203
|
70
|
|
|
|
|
206
|
@{$self}[0, 1, 2], |
1204
|
70
|
|
|
|
|
557
|
@{$self}[$opt->{column} ? |
1205
|
|
|
|
|
|
|
($opt->{finish} ? (3, 4, 5) : (3)) : |
1206
|
70
|
100
|
100
|
|
|
254
|
($opt->{finish} ? (4) : ())] |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
); |
1208
|
|
|
|
|
|
|
} |
1209
|
6
|
|
|
|
|
67
|
} |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
# Constructor for iterating through the subs' files. |
1212
|
|
|
|
|
|
|
sub subs_files_iter { |
1213
|
5
|
|
|
5
|
1
|
12
|
my ($self, $opt) = @_; |
1214
|
5
|
|
|
|
|
25
|
bless { |
1215
|
|
|
|
|
|
|
it => $self->__subs_files_full, |
1216
|
|
|
|
|
|
|
ix => 0, |
1217
|
|
|
|
|
|
|
cb => __file_iter_callback($opt), |
1218
|
|
|
|
|
|
|
}, 'PPI::Xref::SubsFilesIter'; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
# Constructor for iterating through the packages' files. |
1222
|
|
|
|
|
|
|
sub packages_files_iter { |
1223
|
1
|
|
|
1
|
1
|
2
|
my ($self, $opt) = @_; |
1224
|
1
|
|
|
|
|
5
|
bless { |
1225
|
|
|
|
|
|
|
it => $self->__packages_files_full, |
1226
|
|
|
|
|
|
|
ix => 0, |
1227
|
|
|
|
|
|
|
cb => __file_iter_callback($opt), |
1228
|
|
|
|
|
|
|
}, 'PPI::Xref::PackagesFilesIter'; |
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
# Generates all the inclusion files and caches them. |
1232
|
|
|
|
|
|
|
# The inclusion files are the file id, followed all its inclusions. |
1233
|
|
|
|
|
|
|
# Sorting is by filename, line, column, and include_string (e.g. Data::Dumper). |
1234
|
|
|
|
|
|
|
sub __incs_files_full { |
1235
|
1
|
|
|
1
|
|
3
|
my ($self) = @_; |
1236
|
1
|
50
|
|
|
|
7
|
unless (defined $self->{result_cache}{__incs_files}) { |
1237
|
1
|
|
|
|
|
2
|
my @cache; |
1238
|
1
|
|
|
|
|
6
|
for my $f ($self->__file_ids) { |
1239
|
6
|
|
|
|
|
10
|
for my $i (@{ $self->{file_incs}{$f} }) { |
|
6
|
|
|
|
|
20
|
|
1240
|
|
|
|
|
|
|
push @cache, [ $f, # 0: fileid1 |
1241
|
7
|
|
|
|
|
12
|
@{$i}[1, # 1: line |
|
7
|
|
|
|
|
32
|
|
1242
|
|
|
|
|
|
|
7, # 2: fileid2 |
1243
|
|
|
|
|
|
|
0, # 3: stmt |
1244
|
|
|
|
|
|
|
5, # 4: include_string |
1245
|
|
|
|
|
|
|
2, # 5: col |
1246
|
|
|
|
|
|
|
3, # 6: line |
1247
|
|
|
|
|
|
|
4, # 7: col |
1248
|
|
|
|
|
|
|
] ], |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
$self->{result_cache}{__incs_files} = [ |
1252
|
1
|
50
|
66
|
|
|
8
|
sort { $FILE_BY_ID{$a->[0]} cmp $FILE_BY_ID{$b->[0]} || |
|
11
|
|
33
|
|
|
60
|
|
1253
|
|
|
|
|
|
|
$a->[1] <=> $b->[1] || # line |
1254
|
|
|
|
|
|
|
$a->[2] <=> $b->[2] || # column |
1255
|
|
|
|
|
|
|
$a->[5] cmp $b->[5] } # include_string |
1256
|
|
|
|
|
|
|
@cache ]; |
1257
|
|
|
|
|
|
|
} |
1258
|
1
|
|
|
|
|
7
|
return $self->{result_cache}{__incs_files}; |
1259
|
|
|
|
|
|
|
} |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
# Callback generator for inclusion files. |
1262
|
|
|
|
|
|
|
# Selects the desired fields, and concatenates the fields with the separator. |
1263
|
|
|
|
|
|
|
sub __incs_files_iter_callback { |
1264
|
1
|
|
|
1
|
|
3
|
my $opt = shift; |
1265
|
|
|
|
|
|
|
sub { |
1266
|
7
|
|
|
7
|
|
12
|
my $self = shift; |
1267
|
|
|
|
|
|
|
join($opt->{separator} // "\t", |
1268
|
7
|
|
|
|
|
30
|
@{$self}[0..4], |
1269
|
7
|
|
|
|
|
84
|
@{$self}[$opt->{column} ? |
1270
|
|
|
|
|
|
|
($opt->{finish} ? (5, 6, 7) : (5)) : |
1271
|
7
|
0
|
50
|
|
|
34
|
($opt->{finish} ? (6) : ())], |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
); |
1273
|
|
|
|
|
|
|
} |
1274
|
1
|
|
|
|
|
13
|
} |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
package PPI::Xref::IncsFilesIterResult { |
1277
|
32
|
|
|
32
|
|
18526
|
use parent -norequire, 'PPI::Xref::IterResultBase'; |
|
32
|
|
|
|
|
55
|
|
|
32
|
|
|
|
|
162
|
|
1278
|
|
|
|
|
|
|
} |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
package PPI::Xref::IncsFilesIter { |
1281
|
|
|
|
|
|
|
# Iterator stepper for iterating through the inclusion files. |
1282
|
|
|
|
|
|
|
# Converts the file ids to filenames, and returns iterator result. |
1283
|
|
|
|
|
|
|
sub next { |
1284
|
|
|
|
|
|
|
# NOTE: while this is an iterator in the sense of returning the next |
1285
|
|
|
|
|
|
|
# result, this does not compute the next result because all the results |
1286
|
|
|
|
|
|
|
# have already been computed when the iterator was constructed. |
1287
|
8
|
|
|
8
|
|
373
|
my $self = shift; |
1288
|
8
|
100
|
|
|
|
14
|
if ($self->{ix} < @{$self->{it}}) { |
|
8
|
|
|
|
|
32
|
|
1289
|
7
|
|
|
|
|
12
|
my @it = @{ $self->{it}->[$self->{ix}++] }; |
|
7
|
|
|
|
|
26
|
|
1290
|
7
|
|
|
|
|
17
|
$it[0] = $FILE_BY_ID{$it[0]}; |
1291
|
7
|
|
|
|
|
17
|
$it[2] = $FILE_BY_ID{$it[2]}; |
1292
|
|
|
|
|
|
|
return bless { it => \@it, cb => $self->{cb} }, |
1293
|
7
|
|
|
|
|
39
|
'PPI::Xref::IncsFilesIterResult' |
1294
|
|
|
|
|
|
|
} |
1295
|
1
|
|
|
|
|
4
|
return; |
1296
|
|
|
|
|
|
|
} |
1297
|
|
|
|
|
|
|
} |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
# Constructor for iterating through the inclusion files. |
1300
|
|
|
|
|
|
|
sub incs_files_iter { |
1301
|
1
|
|
|
1
|
1
|
2
|
my ($self, $opt) = @_; |
1302
|
1
|
|
|
|
|
5
|
bless { |
1303
|
|
|
|
|
|
|
it => $self->__incs_files_full, |
1304
|
|
|
|
|
|
|
ix => 0, |
1305
|
|
|
|
|
|
|
cb => __incs_files_iter_callback($opt), |
1306
|
|
|
|
|
|
|
}, 'PPI::Xref::IncsFilesIter'; |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
# Recursive generator for inclusion chains. If there are inclusions |
1310
|
|
|
|
|
|
|
# from this file, recurse for them; if not, aggregate into the result. |
1311
|
|
|
|
|
|
|
sub __incs_chains_recurse { |
1312
|
0
|
|
|
0
|
|
0
|
my ($self, $file_id, $path, $seen, $result) = @_; |
1313
|
|
|
|
|
|
|
my @s = |
1314
|
|
|
|
|
|
|
exists $self->{file_incs}{$file_id} ? |
1315
|
0
|
0
|
|
|
|
0
|
@{ $self->{file_incs}{$file_id} } : (); |
|
0
|
|
|
|
|
0
|
|
1316
|
0
|
|
|
|
|
0
|
$seen->{$file_id}++; |
1317
|
|
|
|
|
|
|
# print "recurse: $FILE_BY_ID{$file_id} path: [@{[map { $FILE_BY_ID{$_} // $_ } @$path]}] \n"; |
1318
|
0
|
|
|
|
|
0
|
my $s = 0; |
1319
|
0
|
|
|
|
|
0
|
for my $i (@s) { |
1320
|
0
|
|
|
|
|
0
|
my ($line, $next_file_id) = ($i->[1], $i->[-1]); |
1321
|
|
|
|
|
|
|
# print "recurse: $FILE_BY_ID{$file_id}:$line -> $FILE_BY_ID{$next_file_id} path: [@{[map { $FILE_BY_ID{$_} // $_ } @$path]}] seen: [@{[sort map { $FILE_BY_ID{$_} } keys %$seen]}]\n"; |
1322
|
|
|
|
|
|
|
# E.g. Carp uses strict, strict requires Carp. |
1323
|
0
|
0
|
|
|
|
0
|
unless ($seen->{$next_file_id}++) { |
1324
|
0
|
|
|
|
|
0
|
$self->__incs_chains_recurse($next_file_id, [ @$path, $line, $next_file_id ], $seen, $result); |
1325
|
0
|
|
|
|
|
0
|
$s++; |
1326
|
|
|
|
|
|
|
} |
1327
|
|
|
|
|
|
|
} |
1328
|
0
|
0
|
|
|
|
0
|
if ($s == 0) { # If this was a leaf (no paths leading out), aggregrate result. |
1329
|
0
|
|
|
|
|
0
|
push @{$result}, [ @$path ]; |
|
0
|
|
|
|
|
0
|
|
1330
|
|
|
|
|
|
|
} |
1331
|
0
|
|
|
|
|
0
|
delete $seen->{$file_id}; |
1332
|
|
|
|
|
|
|
} |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
sub __incs_deps { |
1335
|
4
|
|
|
4
|
|
9
|
my ($self) = @_; |
1336
|
4
|
50
|
|
|
|
22
|
unless (defined $self->{result_cache}{__incs_deps}) { |
1337
|
4
|
|
|
|
|
7
|
my %pred; |
1338
|
|
|
|
|
|
|
my %succ; |
1339
|
0
|
|
|
|
|
0
|
my %line; |
1340
|
4
|
|
|
|
|
21
|
for my $fi ($self->__file_ids) { |
1341
|
25
|
100
|
|
|
|
81
|
if (exists $self->{file_incs}{$fi}) { |
1342
|
19
|
|
|
|
|
25
|
for my $g (@{ $self->{file_incs}{$fi} }) { |
|
19
|
|
|
|
|
73
|
|
1343
|
21
|
|
|
|
|
31
|
my ($gl, $gi) = @{ $g }[ 1, 7 ]; |
|
21
|
|
|
|
|
47
|
|
1344
|
21
|
|
|
|
|
60
|
$succ{$fi}{$gi}{$gl}++; |
1345
|
21
|
|
|
|
|
50
|
$pred{$gi}{$fi}{$gl}++; |
1346
|
21
|
|
|
|
|
69
|
$line{$fi}{$gl}{$gi}++; |
1347
|
|
|
|
|
|
|
} |
1348
|
|
|
|
|
|
|
} |
1349
|
|
|
|
|
|
|
} |
1350
|
4
|
|
|
|
|
13
|
my %singleton; |
1351
|
|
|
|
|
|
|
my %leaf; |
1352
|
0
|
|
|
|
|
0
|
my %root; |
1353
|
0
|
|
|
|
|
0
|
my %branch; |
1354
|
4
|
|
|
|
|
15
|
for my $s ($self->__file_ids) { |
1355
|
25
|
100
|
|
|
|
76
|
my @s = exists $succ{$s} ? keys %{$succ{$s}} : (); |
|
9
|
|
|
|
|
35
|
|
1356
|
25
|
100
|
|
|
|
65
|
my @p = exists $pred{$s} ? keys %{$pred{$s}} : (); |
|
15
|
|
|
|
|
47
|
|
1357
|
25
|
100
|
|
|
|
69
|
if (@s == 0) { |
|
|
100
|
|
|
|
|
|
1358
|
16
|
100
|
|
|
|
38
|
if (@p == 0) { |
1359
|
7
|
|
|
|
|
18
|
$singleton{$s}++; |
1360
|
|
|
|
|
|
|
} else { |
1361
|
9
|
|
|
|
|
27
|
$leaf{$s}++; |
1362
|
|
|
|
|
|
|
} |
1363
|
|
|
|
|
|
|
} elsif (@p == 0) { |
1364
|
3
|
|
|
|
|
11
|
$root{$s}++; |
1365
|
|
|
|
|
|
|
} else { |
1366
|
6
|
|
|
|
|
18
|
$branch{$s}++; |
1367
|
|
|
|
|
|
|
} |
1368
|
|
|
|
|
|
|
} |
1369
|
|
|
|
|
|
|
$self->{result_cache}{__incs_deps} = { |
1370
|
4
|
|
|
|
|
47
|
pred => \%pred, |
1371
|
|
|
|
|
|
|
succ => \%succ, |
1372
|
|
|
|
|
|
|
line => \%line, |
1373
|
|
|
|
|
|
|
singleton => \%singleton, |
1374
|
|
|
|
|
|
|
leaf => \%leaf, |
1375
|
|
|
|
|
|
|
root => \%root, |
1376
|
|
|
|
|
|
|
branch => \%branch, |
1377
|
|
|
|
|
|
|
parent => $self, |
1378
|
|
|
|
|
|
|
}; |
1379
|
|
|
|
|
|
|
} |
1380
|
4
|
|
|
|
|
31
|
return $self->{result_cache}{__incs_deps}; |
1381
|
|
|
|
|
|
|
} |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
package PPI::Xref::IncsDeps { |
1384
|
|
|
|
|
|
|
sub files { |
1385
|
1
|
|
|
1
|
|
286
|
my ($self) = @_; |
1386
|
1
|
|
|
|
|
5
|
return $self->{parent}->files; |
1387
|
|
|
|
|
|
|
} |
1388
|
|
|
|
|
|
|
sub __file_id { |
1389
|
8
|
|
|
8
|
|
11
|
my ($self, $file) = @_; |
1390
|
8
|
|
|
|
|
22
|
return $self->{parent}{file_id}{$file}; |
1391
|
|
|
|
|
|
|
} |
1392
|
|
|
|
|
|
|
sub __by_file { |
1393
|
0
|
|
|
0
|
|
0
|
my ($self, $key, $file) = @_; |
1394
|
0
|
|
|
|
|
0
|
my $file_id = $self->__file_id($file); |
1395
|
0
|
0
|
0
|
|
|
0
|
return unless defined $file_id && exists $self->{$key}{$file_id}; |
1396
|
0
|
|
|
|
|
0
|
return keys %{ $self->{$key}{$file_id} }; |
|
0
|
|
|
|
|
0
|
|
1397
|
|
|
|
|
|
|
} |
1398
|
|
|
|
|
|
|
sub __filenames { |
1399
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1400
|
0
|
|
|
|
|
0
|
return map { $FILE_BY_ID{$_} } @_; |
|
0
|
|
|
|
|
0
|
|
1401
|
|
|
|
|
|
|
} |
1402
|
|
|
|
|
|
|
sub __predecessors { |
1403
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
1404
|
0
|
|
|
|
|
0
|
return _$self->__by_file(pred => $file); |
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
sub __successors { |
1407
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
1408
|
0
|
|
|
|
|
0
|
return $self->__by_file(succ => $file); |
1409
|
|
|
|
|
|
|
} |
1410
|
|
|
|
|
|
|
sub predecessors { |
1411
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
1412
|
0
|
|
|
|
|
0
|
return $self->__filenames(_$self->__predecessors($file)); |
1413
|
|
|
|
|
|
|
} |
1414
|
|
|
|
|
|
|
sub successors { |
1415
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
1416
|
0
|
|
|
|
|
0
|
return $self->__filenames($self->__successors($file)); |
1417
|
|
|
|
|
|
|
} |
1418
|
|
|
|
|
|
|
sub __files { |
1419
|
0
|
|
|
0
|
|
0
|
my ($self, $key) = @_; |
1420
|
|
|
|
|
|
|
return exists $self->{$key} ? |
1421
|
0
|
0
|
|
|
|
0
|
map { $FILE_BY_ID{$_} } keys %{ $self->{$key} } : (); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1422
|
|
|
|
|
|
|
} |
1423
|
|
|
|
|
|
|
sub __roots { |
1424
|
2
|
|
|
2
|
|
5
|
my ($self) = @_; |
1425
|
2
|
50
|
|
|
|
21
|
return exists $self->{root} ? keys %{ $self->{root} } : (); |
|
2
|
|
|
|
|
16
|
|
1426
|
|
|
|
|
|
|
} |
1427
|
|
|
|
|
|
|
sub __singletons { |
1428
|
2
|
|
|
2
|
|
5
|
my ($self) = @_; |
1429
|
2
|
50
|
|
|
|
9
|
return exists $self->{singleton} ? keys %{ $self->{singleton} } : (); |
|
2
|
|
|
|
|
10
|
|
1430
|
|
|
|
|
|
|
} |
1431
|
|
|
|
|
|
|
sub roots { |
1432
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
1433
|
0
|
|
|
|
|
0
|
return $self->__files('root'); |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
sub leaves { |
1436
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
1437
|
0
|
|
|
|
|
0
|
return $self->__files('leaf'); |
1438
|
|
|
|
|
|
|
} |
1439
|
|
|
|
|
|
|
sub singletons { |
1440
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
1441
|
0
|
|
|
|
|
0
|
return $self->__files('singleton'); |
1442
|
|
|
|
|
|
|
} |
1443
|
|
|
|
|
|
|
sub branches { |
1444
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
1445
|
0
|
|
|
|
|
0
|
return $self->__files('branch'); |
1446
|
|
|
|
|
|
|
} |
1447
|
|
|
|
|
|
|
sub __file_kind { |
1448
|
10
|
|
|
10
|
|
16
|
my ($self, $file_id) = @_; |
1449
|
10
|
100
|
|
|
|
33
|
return unless defined $file_id; |
1450
|
9
|
100
|
|
|
|
27
|
return 'branch' if exists $self->{branch} {$file_id}; |
1451
|
7
|
100
|
|
|
|
26
|
return 'leaf' if exists $self->{leaf} {$file_id}; |
1452
|
4
|
100
|
|
|
|
19
|
return 'root' if exists $self->{root} {$file_id}; |
1453
|
1
|
50
|
|
|
|
7
|
return 'singleton' if exists $self->{singleton}{$file_id}; |
1454
|
0
|
|
|
|
|
0
|
return; |
1455
|
|
|
|
|
|
|
} |
1456
|
|
|
|
|
|
|
sub file_kind { |
1457
|
8
|
|
|
8
|
|
648
|
my ($self, $file) = @_; |
1458
|
8
|
|
|
|
|
18
|
return $self->__file_kind($self->__file_id($file)); |
1459
|
|
|
|
|
|
|
} |
1460
|
|
|
|
|
|
|
} |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
sub incs_deps { |
1463
|
4
|
|
|
4
|
1
|
10
|
my ($self) = @_; |
1464
|
4
|
|
|
|
|
18
|
bless $self->__incs_deps, 'PPI::Xref::IncsDeps'; |
1465
|
|
|
|
|
|
|
} |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
sub __incs_chains_iter { |
1468
|
2
|
|
|
2
|
|
5
|
my ($self, $opt) = @_; |
1469
|
2
|
|
|
|
|
4
|
my %iter; |
1470
|
2
|
|
|
|
|
9
|
my $deps = $self->incs_deps; |
1471
|
2
|
50
|
|
|
|
9
|
if (defined $deps) { |
1472
|
|
|
|
|
|
|
$iter{next} = sub { |
1473
|
10
|
|
|
10
|
|
18
|
my ($iterself) = @_; |
1474
|
10
|
|
|
|
|
37
|
until ($iterself->{done}) { |
1475
|
12
|
100
|
100
|
|
|
40
|
unless (defined $iterself->{path} && @{ $iterself->{path} }) { |
|
10
|
|
|
|
|
37
|
|
1476
|
4
|
100
|
|
|
|
16
|
unless (defined $iterself->{roots}) { |
1477
|
2
|
|
|
|
|
10
|
my @roots = ( |
1478
|
|
|
|
|
|
|
$deps->__roots, |
1479
|
|
|
|
|
|
|
$deps->__singletons, |
1480
|
|
|
|
|
|
|
); |
1481
|
2
|
|
|
|
|
6
|
my %roots; |
1482
|
2
|
|
|
|
|
6
|
@roots{@roots} = (); |
1483
|
2
|
50
|
|
|
|
11
|
if (exists $self->{__process}) { |
1484
|
2
|
|
|
|
|
6
|
for my $id (keys %{ $self->{__process} }) { |
|
2
|
|
|
|
|
8
|
|
1485
|
2
|
50
|
|
|
|
12
|
push @roots, $id unless exists $roots{$id}; |
1486
|
|
|
|
|
|
|
} |
1487
|
|
|
|
|
|
|
} |
1488
|
2
|
|
|
|
|
9
|
$iterself->{roots} = \@roots; |
1489
|
|
|
|
|
|
|
} |
1490
|
4
|
|
|
|
|
8
|
my $root = shift @{ $iterself->{roots} }; |
|
4
|
|
|
|
|
9
|
|
1491
|
4
|
100
|
|
|
|
13
|
unless (defined $root) { |
1492
|
2
|
|
|
|
|
5
|
$iterself->{done}++; |
1493
|
2
|
|
|
|
|
8
|
return; |
1494
|
|
|
|
|
|
|
} |
1495
|
2
|
|
|
|
|
8
|
$iterself->{path} = [ $root ]; |
1496
|
|
|
|
|
|
|
# E.g. Carp uses strict, strict requires Carp, and also |
1497
|
|
|
|
|
|
|
# the dependency trees are very probably not clean DAGs. |
1498
|
2
|
|
|
|
|
12
|
$iterself->{seen} = { $root => { 0 => 1 } }; |
1499
|
|
|
|
|
|
|
}; |
1500
|
10
|
|
|
|
|
18
|
while (@{ $iterself->{path} }) { |
|
32
|
|
|
|
|
84
|
|
1501
|
30
|
|
|
|
|
51
|
my $curr = $iterself->{path}[-1]; |
1502
|
30
|
|
|
|
|
37
|
my $pushed = 0; |
1503
|
|
|
|
|
|
|
SUCC: { |
1504
|
30
|
100
|
|
|
|
34
|
if (exists $deps->{line}{$curr}) { |
|
30
|
|
|
|
|
78
|
|
1505
|
22
|
|
|
|
|
30
|
for my $line (sort { $a <=> $b } |
|
38
|
|
|
|
|
82
|
|
1506
|
22
|
|
|
|
|
84
|
keys %{ $deps->{line}{$curr} }) { |
1507
|
42
|
|
|
|
|
50
|
for my $succ (sort { $a cmp $b } |
|
0
|
|
|
|
|
0
|
|
1508
|
42
|
|
|
|
|
115
|
keys %{ $deps->{line}{$curr}{$line} }) { |
1509
|
42
|
100
|
|
|
|
141
|
unless ($iterself->{seen}{$succ}{$line}++) { |
1510
|
14
|
|
|
|
|
16
|
push @{ $iterself->{path} }, $line, $succ; |
|
14
|
|
|
|
|
34
|
|
1511
|
14
|
|
|
|
|
19
|
$pushed++; |
1512
|
14
|
|
|
|
|
31
|
last SUCC; |
1513
|
|
|
|
|
|
|
} |
1514
|
|
|
|
|
|
|
} |
1515
|
|
|
|
|
|
|
} |
1516
|
|
|
|
|
|
|
} |
1517
|
|
|
|
|
|
|
} |
1518
|
30
|
100
|
|
|
|
66
|
unless ($pushed) { |
1519
|
16
|
50
|
|
|
|
19
|
if (my @path = @{ $iterself->{path} }) { |
|
16
|
|
|
|
|
63
|
|
1520
|
16
|
100
|
|
|
|
36
|
@path = reverse @path if $opt->{reverse_chains}; |
1521
|
16
|
100
|
|
|
|
35
|
if (@path > 1) { |
1522
|
14
|
|
|
|
|
17
|
splice @{ $iterself->{path} }, -2; # Double-pop. |
|
14
|
|
|
|
|
26
|
|
1523
|
14
|
100
|
|
|
|
39
|
if ($self->{lastpush}) { |
1524
|
8
|
|
|
|
|
11
|
$self->{lastpush} = $pushed; |
1525
|
8
|
|
|
|
|
47
|
return @path; |
1526
|
|
|
|
|
|
|
} |
1527
|
|
|
|
|
|
|
} else { |
1528
|
2
|
|
|
|
|
7
|
$iterself->{path} = []; |
1529
|
2
|
|
|
|
|
8
|
my $kind = $deps->__file_kind($curr); |
1530
|
2
|
50
|
33
|
|
|
17
|
if (defined $kind && $kind eq 'singleton') { |
1531
|
0
|
|
|
|
|
0
|
return @path; |
1532
|
|
|
|
|
|
|
} |
1533
|
|
|
|
|
|
|
} |
1534
|
|
|
|
|
|
|
} |
1535
|
|
|
|
|
|
|
} |
1536
|
22
|
|
|
|
|
43
|
$self->{lastpush} = $pushed; |
1537
|
|
|
|
|
|
|
} # while |
1538
|
|
|
|
|
|
|
} |
1539
|
2
|
|
|
|
|
20
|
}; |
1540
|
|
|
|
|
|
|
} |
1541
|
2
|
|
|
|
|
12
|
return \%iter; |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
# Callback generator for inclusion chains. |
1545
|
|
|
|
|
|
|
# Simply concatenates the fields with the separator. |
1546
|
|
|
|
|
|
|
sub __incs_chains_iter_callback { |
1547
|
2
|
|
|
2
|
|
5
|
my $opt = shift; |
1548
|
|
|
|
|
|
|
sub { |
1549
|
8
|
|
|
8
|
|
12
|
my $self = shift; |
1550
|
8
|
|
50
|
|
|
42
|
join($opt->{separator} // "\t", @{$self} ); |
|
8
|
|
|
|
|
122
|
|
1551
|
|
|
|
|
|
|
} |
1552
|
2
|
|
|
|
|
22
|
} |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
package PPI::Xref::IncsChainsIterResult { |
1555
|
32
|
|
|
32
|
|
68192
|
use parent -norequire, 'PPI::Xref::IterResultBase'; |
|
32
|
|
|
|
|
67
|
|
|
32
|
|
|
|
|
210
|
|
1556
|
|
|
|
|
|
|
} |
1557
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
package PPI::Xref::IncsChainsIter { |
1559
|
|
|
|
|
|
|
# Iterator stepper for iterating through the inclusion chains. |
1560
|
|
|
|
|
|
|
# Converts the file ids to filenames, and returns iterator result. |
1561
|
|
|
|
|
|
|
sub next { |
1562
|
10
|
|
|
10
|
|
606
|
my $self = shift; |
1563
|
10
|
100
|
|
|
|
23
|
if (my @it = $self->{it}{next}->($self)) { |
1564
|
8
|
|
|
|
|
22
|
for (my $i = 0; $i < @it; $i += 2) { |
1565
|
26
|
|
|
|
|
74
|
$it[$i] = $FILE_BY_ID{$it[$i]}; |
1566
|
|
|
|
|
|
|
} |
1567
|
|
|
|
|
|
|
return bless { it => \@it, cb => $self->{cb} }, |
1568
|
8
|
|
|
|
|
43
|
'PPI::Xref::IncsChainsIterResult'; |
1569
|
|
|
|
|
|
|
} |
1570
|
2
|
|
|
|
|
6
|
return; |
1571
|
|
|
|
|
|
|
} |
1572
|
|
|
|
|
|
|
} |
1573
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
# Constructor for iterating through the inclusion chains. |
1575
|
|
|
|
|
|
|
sub incs_chains_iter { |
1576
|
2
|
|
|
2
|
1
|
6
|
my ($self, $opt) = @_; |
1577
|
2
|
|
|
|
|
12
|
bless { |
1578
|
|
|
|
|
|
|
it => $self->__incs_chains_iter($opt), |
1579
|
|
|
|
|
|
|
ix => 0, |
1580
|
|
|
|
|
|
|
cb => __incs_chains_iter_callback($opt), |
1581
|
|
|
|
|
|
|
}, 'PPI::Xref::IncsChainsIter'; |
1582
|
|
|
|
|
|
|
} |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
sub looks_like_cache_file { |
1585
|
3
|
|
|
3
|
0
|
6
|
my ($self, $file) = @_; |
1586
|
|
|
|
|
|
|
|
1587
|
3
|
|
|
|
|
7
|
my $cache_directory = $self->{opt}{cache_directory}; |
1588
|
3
|
50
|
|
|
|
8
|
return unless defined $cache_directory; |
1589
|
|
|
|
|
|
|
|
1590
|
3
|
50
|
|
|
|
8
|
return 0 if $file =~ m{\.\.}; |
1591
|
|
|
|
|
|
|
|
1592
|
3
|
|
|
|
|
31
|
return $file =~ m{^\Q$cache_directory\E/.+\Q$CACHE_EXT\E$}; |
1593
|
|
|
|
|
|
|
} |
1594
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
sub cache_delete { |
1596
|
3
|
|
|
3
|
1
|
2381
|
my $self = shift; |
1597
|
3
|
|
|
|
|
7
|
my $cache_directory = $self->{opt}{cache_directory}; |
1598
|
3
|
50
|
|
|
|
10
|
unless (defined $cache_directory) { |
1599
|
0
|
|
|
|
|
0
|
warn "$Sub: cache_directory undefined\n"; |
1600
|
0
|
|
|
|
|
0
|
return; |
1601
|
|
|
|
|
|
|
} |
1602
|
3
|
|
|
|
|
5
|
my $delete_count = 0; |
1603
|
3
|
|
|
|
|
6
|
for my $file (@_) { |
1604
|
3
|
50
|
33
|
|
|
82
|
if (!File::Spec->file_name_is_absolute($file) || |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
1605
|
|
|
|
|
|
|
$file =~ m{\.\.} || |
1606
|
|
|
|
|
|
|
($file !~ m{_p[ml](?:\Q$CACHE_EXT\E)?$} && |
1607
|
|
|
|
|
|
|
$file !~ m{.p[ml]$})) { |
1608
|
|
|
|
|
|
|
# Paranoia check one. |
1609
|
0
|
|
|
|
|
0
|
warn "$Sub: Skipping unexpected file: '$file'\n"; |
1610
|
0
|
|
|
|
|
0
|
next; |
1611
|
|
|
|
|
|
|
} |
1612
|
3
|
100
|
|
|
|
23
|
my $cache_file = |
1613
|
|
|
|
|
|
|
$file =~ /\Q$CACHE_EXT\E$/ ? |
1614
|
|
|
|
|
|
|
$file : $self->__cache_filename($file); |
1615
|
|
|
|
|
|
|
# Paranoia check two. Both paranoia checks are needed. |
1616
|
3
|
50
|
|
|
|
11
|
unless ($self->looks_like_cache_file($cache_file)) { |
1617
|
0
|
|
|
|
|
0
|
warn "$Sub: Skipping unexpected cache file: '$cache_file'\n"; |
1618
|
0
|
|
|
|
|
0
|
next; |
1619
|
|
|
|
|
|
|
} |
1620
|
3
|
50
|
|
|
|
12
|
if ($self->{opt}{cache_verbose}) { |
1621
|
3
|
|
|
|
|
15
|
print "cache_delete: deleting $cache_file\n"; |
1622
|
|
|
|
|
|
|
} |
1623
|
3
|
100
|
|
|
|
227
|
if (unlink $cache_file) { |
1624
|
2
|
|
|
|
|
4
|
$delete_count++; |
1625
|
2
|
|
|
|
|
8
|
$self->{__cachedeletes}++; |
1626
|
|
|
|
|
|
|
} |
1627
|
|
|
|
|
|
|
} |
1628
|
3
|
|
|
|
|
15
|
return $delete_count; |
1629
|
|
|
|
|
|
|
} |
1630
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
sub __unparse_cache_filename { |
1632
|
1
|
|
|
1
|
|
1157
|
my ($self, $cache_filename) = @_; |
1633
|
|
|
|
|
|
|
|
1634
|
1
|
|
|
|
|
4
|
my $cache_directory = $self->{opt}{cache_directory}; |
1635
|
1
|
50
|
|
|
|
4
|
return unless defined $cache_directory; |
1636
|
|
|
|
|
|
|
|
1637
|
1
|
50
|
|
|
|
16
|
return unless $cache_filename =~ s{\Q$CACHE_EXT\E$}{}; |
1638
|
|
|
|
|
|
|
|
1639
|
1
|
|
|
|
|
4
|
my $cache_prefix_length = $self->{__cache_prefix_length}; |
1640
|
1
|
50
|
|
|
|
3
|
return unless length($cache_filename) > $cache_prefix_length; |
1641
|
|
|
|
|
|
|
|
1642
|
1
|
|
|
|
|
3
|
my $prefix = substr($cache_filename, 0, $cache_prefix_length); |
1643
|
1
|
50
|
|
|
|
65
|
return unless $prefix =~ m{^\Q$cache_directory\E(?:/|\\)$}; |
1644
|
|
|
|
|
|
|
|
1645
|
1
|
|
|
|
|
3
|
my $path = substr($cache_filename, $cache_prefix_length - 1); |
1646
|
|
|
|
|
|
|
|
1647
|
1
|
|
|
|
|
8
|
$path =~ s{_(p[ml])$}{\.$1}; # _pm -> .pm, _pl -> .pl |
1648
|
|
|
|
|
|
|
|
1649
|
1
|
50
|
|
|
|
4
|
if ($^O eq 'MSWin32') { |
1650
|
|
|
|
|
|
|
# \c\a\b -> c:/a/b |
1651
|
0
|
|
|
|
|
0
|
$path =~ s{\\}{/}g; |
1652
|
0
|
0
|
|
|
|
0
|
if ($path =~ m{^/([A-Z])(/.+)}) { |
1653
|
0
|
|
|
|
|
0
|
my $volpath = "$1:$2"; |
1654
|
0
|
0
|
|
|
|
0
|
if (-f $volpath) { |
1655
|
0
|
|
|
|
|
0
|
$path = $volpath; |
1656
|
|
|
|
|
|
|
} |
1657
|
|
|
|
|
|
|
} |
1658
|
|
|
|
|
|
|
} |
1659
|
|
|
|
|
|
|
|
1660
|
1
|
|
|
|
|
8
|
return $path; |
1661
|
|
|
|
|
|
|
} |
1662
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
# Given an xref, find all the cache files under its cache directory, |
1664
|
|
|
|
|
|
|
# and add their filenames to href. |
1665
|
|
|
|
|
|
|
sub find_cache_files { |
1666
|
0
|
|
|
0
|
0
|
|
my ($self, $files) = @_; |
1667
|
|
|
|
|
|
|
|
1668
|
0
|
|
|
|
|
|
my $cache_directory = $self->{opt}{cache_directory}; |
1669
|
0
|
0
|
|
|
|
|
unless (defined $cache_directory) { |
1670
|
0
|
|
|
|
|
|
warn "$Sub: cache_directory undefined\n"; |
1671
|
0
|
|
|
|
|
|
return; |
1672
|
|
|
|
|
|
|
} |
1673
|
|
|
|
|
|
|
|
1674
|
32
|
|
|
32
|
|
28738
|
use File::Find qw[find]; |
|
32
|
|
|
|
|
57
|
|
|
32
|
|
|
|
|
6050
|
|
1675
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
find( |
1677
|
|
|
|
|
|
|
sub { |
1678
|
0
|
0
|
|
0
|
|
|
if (/\.p[ml]\Q$CACHE_EXT\E$/) { |
1679
|
0
|
|
|
|
|
|
my $name = $self->__unparse_cache_filename($File::Find::name); |
1680
|
0
|
|
|
|
|
|
$files->{$name} = $File::Find::name; |
1681
|
|
|
|
|
|
|
} |
1682
|
|
|
|
|
|
|
}, |
1683
|
0
|
|
|
|
|
|
$cache_directory); |
1684
|
|
|
|
|
|
|
} |
1685
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
# Given an xref, find all the pm files under its INC, |
1687
|
|
|
|
|
|
|
# and add their filenames to href. |
1688
|
|
|
|
|
|
|
sub find_system_files { |
1689
|
0
|
|
|
0
|
0
|
|
my ($self, $files) = @_; |
1690
|
|
|
|
|
|
|
|
1691
|
32
|
|
|
32
|
|
191
|
use File::Find qw[find]; |
|
32
|
|
|
|
|
73
|
|
|
32
|
|
|
|
|
4878
|
|
1692
|
|
|
|
|
|
|
|
1693
|
0
|
|
|
|
|
|
for my $d (@{ $self->INC }) { |
|
0
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
find( |
1695
|
|
|
|
|
|
|
sub { |
1696
|
0
|
0
|
|
0
|
|
|
if (/\.p[ml]$/) { |
1697
|
0
|
|
|
|
|
|
$files->{$File::Find::name} = $File::Find::name; |
1698
|
|
|
|
|
|
|
} |
1699
|
|
|
|
|
|
|
}, |
1700
|
0
|
|
|
|
|
|
$d); |
1701
|
|
|
|
|
|
|
} |
1702
|
|
|
|
|
|
|
} |
1703
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
1; |
1705
|
|
|
|
|
|
|
__DATA__ |