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