Eliminate CONDOPs
[p5sagit/p5-mst-13.2.git] / ext / B / B / Xref.pm
CommitLineData
a798dbf2 1package B::Xref;
2
3=head1 NAME
4
5B::Xref - Generates cross reference reports for Perl programs
6
7=head1 SYNOPSIS
8
9perl -MO=Xref[,OPTIONS] foo.pl
10
11=head1 DESCRIPTION
12
13The B::Xref module is used to generate a cross reference listing of all
14definitions and uses of variables, subroutines and formats in a Perl program.
15It is implemented as a backend for the Perl compiler.
16
17The report generated is in the following format:
18
19 File filename1
20 Subroutine subname1
21 Package package1
22 object1 C<line numbers>
23 object2 C<line numbers>
24 ...
25 Package package2
26 ...
27
28Each B<File> section reports on a single file. Each B<Subroutine> section
29reports on a single subroutine apart from the special cases
30"(definitions)" and "(main)". These report, respectively, on subroutine
31definitions found by the initial symbol table walk and on the main part of
32the program or module external to all subroutines.
33
34The report is then grouped by the B<Package> of each variable,
35subroutine or format with the special case "(lexicals)" meaning
36lexical variables. Each B<object> name (implicitly qualified by its
37containing B<Package>) includes its type character(s) at the beginning
38where possible. Lexical variables are easier to track and even
39included dereferencing information where possible.
40
41The C<line numbers> are a comma separated list of line numbers (some
42preceded by code letters) where that object is used in some way.
43Simple uses aren't preceded by a code letter. Introductions (such as
44where a lexical is first defined with C<my>) are indicated with the
45letter "i". Subroutine and method calls are indicated by the character
46"&". Subroutine definitions are indicated by "s" and format
47definitions by "f".
48
49=head1 OPTIONS
50
51Option words are separated by commas (not whitespace) and follow the
52usual conventions of compiler backend options.
53
54=over 8
55
56=item C<-oFILENAME>
57
58Directs output to C<FILENAME> instead of standard output.
59
60=item C<-r>
61
62Raw output. Instead of producing a human-readable report, outputs a line
63in machine-readable form for each definition/use of a variable/sub/format.
64
65=item C<-D[tO]>
66
67(Internal) debug options, probably only useful if C<-r> included.
68The C<t> option prints the object on the top of the stack as it's
69being tracked. The C<O> option prints each operator as it's being
70processed in the execution order of the program.
71
72=back
73
74=head1 BUGS
75
76Non-lexical variables are quite difficult to track through a program.
77Sometimes the type of a non-lexical variable's use is impossible to
78determine. Introductions of non-lexical non-scalars don't seem to be
79reported properly.
80
81=head1 AUTHOR
82
83Malcolm Beattie, mbeattie@sable.ox.ac.uk.
84
85=cut
86
87use strict;
4c1f658f 88use B qw(peekop class comppadlist main_start svref_2object walksymtable
89 OPpLVAL_INTRO SVf_POK
90 );
a798dbf2 91
92sub UNKNOWN { ["?", "?", "?"] }
93
94my @pad; # lexicals in current pad
95 # as ["(lexical)", type, name]
96my %done; # keyed by $$op: set when each $op is done
97my $top = UNKNOWN; # shadows top element of stack as
98 # [pack, type, name] (pack can be "(lexical)")
99my $file; # shadows current filename
100my $line; # shadows current line number
101my $subname; # shadows current sub name
102my %table; # Multi-level hash to record all uses etc.
103my @todo = (); # List of CVs that need processing
104
105my %code = (intro => "i", used => "",
106 subdef => "s", subused => "&",
107 formdef => "f", meth => "->");
108
109
110# Options
111my ($debug_op, $debug_top, $nodefs, $raw);
112
113sub process {
114 my ($var, $event) = @_;
115 my ($pack, $type, $name) = @$var;
116 if ($type eq "*") {
117 if ($event eq "used") {
118 return;
119 } elsif ($event eq "subused") {
120 $type = "&";
121 }
122 }
123 $type =~ s/(.)\*$/$1/g;
124 if ($raw) {
125 printf "%-16s %-12s %5d %-12s %4s %-16s %s\n",
126 $file, $subname, $line, $pack, $type, $name, $event;
127 } else {
128 # Wheee
129 push(@{$table{$file}->{$subname}->{$pack}->{$type.$name}->{$event}},
130 $line);
131 }
132}
133
134sub load_pad {
135 my $padlist = shift;
136 my ($namelistav, @namelist, $ix);
137 @pad = ();
138 return if class($padlist) eq "SPECIAL";
139 ($namelistav) = $padlist->ARRAY;
140 @namelist = $namelistav->ARRAY;
141 for ($ix = 1; $ix < @namelist; $ix++) {
142 my $namesv = $namelist[$ix];
143 next if class($namesv) eq "SPECIAL";
51e5a3db 144 my ($type, $name) = $namesv->PV =~ /^(.)([^\0]*)(\0.*)?$/;
a798dbf2 145 $pad[$ix] = ["(lexical)", $type, $name];
146 }
147}
148
149sub xref {
150 my $start = shift;
151 my $op;
152 for ($op = $start; $$op; $op = $op->next) {
153 last if $done{$$op}++;
154 warn sprintf("top = [%s, %s, %s]\n", @$top) if $debug_top;
155 warn peekop($op), "\n" if $debug_op;
156 my $ppname = $op->ppaddr;
1a67a97c 157 if ($ppname =~ /^pp_(or|and|mapwhile|grepwhile|range|cond_expr)$/) {
a798dbf2 158 xref($op->other);
159 } elsif ($ppname eq "pp_match" || $ppname eq "pp_subst") {
160 xref($op->pmreplstart);
161 } elsif ($ppname eq "pp_substcont") {
162 xref($op->other->pmreplstart);
163 $op = $op->other;
164 redo;
a798dbf2 165 } elsif ($ppname eq "pp_enterloop") {
166 xref($op->redoop);
167 xref($op->nextop);
168 xref($op->lastop);
169 } elsif ($ppname eq "pp_subst") {
170 xref($op->pmreplstart);
171 } else {
172 no strict 'refs';
173 &$ppname($op) if defined(&$ppname);
174 }
175 }
176}
177
178sub xref_cv {
179 my $cv = shift;
180 my $pack = $cv->GV->STASH->NAME;
181 $subname = ($pack eq "main" ? "" : "$pack\::") . $cv->GV->NAME;
182 load_pad($cv->PADLIST);
183 xref($cv->START);
184 $subname = "(main)";
185}
186
187sub xref_object {
188 my $cvref = shift;
189 xref_cv(svref_2object($cvref));
190}
191
192sub xref_main {
193 $subname = "(main)";
194 load_pad(comppadlist);
195 xref(main_start);
196 while (@todo) {
197 xref_cv(shift @todo);
198 }
199}
200
201sub pp_nextstate {
202 my $op = shift;
203 $file = $op->filegv->SV->PV;
204 $line = $op->line;
205 $top = UNKNOWN;
206}
207
208sub pp_padsv {
209 my $op = shift;
210 $top = $pad[$op->targ];
211 process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
212}
213
214sub pp_padav { pp_padsv(@_) }
215sub pp_padhv { pp_padsv(@_) }
216
217sub deref {
218 my ($var, $as) = @_;
219 $var->[1] = $as . $var->[1];
220 process($var, "used");
221}
222
223sub pp_rv2cv { deref($top, "&"); }
224sub pp_rv2hv { deref($top, "%"); }
225sub pp_rv2sv { deref($top, "\$"); }
226sub pp_rv2av { deref($top, "\@"); }
227sub pp_rv2gv { deref($top, "*"); }
228
229sub pp_gvsv {
230 my $op = shift;
231 my $gv = $op->gv;
232 $top = [$gv->STASH->NAME, '$', $gv->NAME];
233 process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
234}
235
236sub pp_gv {
237 my $op = shift;
238 my $gv = $op->gv;
239 $top = [$gv->STASH->NAME, "*", $gv->NAME];
240 process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
241}
242
243sub pp_const {
244 my $op = shift;
245 my $sv = $op->sv;
246 $top = ["?", "",
247 (class($sv) ne "SPECIAL" && $sv->FLAGS & SVf_POK) ? $sv->PV : "?"];
248}
249
250sub pp_method {
251 my $op = shift;
252 $top = ["(method)", "->".$top->[1], $top->[2]];
253}
254
255sub pp_entersub {
256 my $op = shift;
257 if ($top->[1] eq "m") {
258 process($top, "meth");
259 } else {
260 process($top, "subused");
261 }
262 $top = UNKNOWN;
263}
264
265#
266# Stuff for cross referencing definitions of variables and subs
267#
268
269sub B::GV::xref {
270 my $gv = shift;
271 my $cv = $gv->CV;
272 if ($$cv) {
273 #return if $done{$$cv}++;
274 $file = $gv->FILEGV->SV->PV;
275 $line = $gv->LINE;
276 process([$gv->STASH->NAME, "&", $gv->NAME], "subdef");
277 push(@todo, $cv);
278 }
279 my $form = $gv->FORM;
280 if ($$form) {
281 return if $done{$$form}++;
282 $file = $gv->FILEGV->SV->PV;
283 $line = $gv->LINE;
284 process([$gv->STASH->NAME, "", $gv->NAME], "formdef");
285 }
286}
287
288sub xref_definitions {
289 my ($pack, %exclude);
290 return if $nodefs;
291 $subname = "(definitions)";
292 foreach $pack (qw(B O AutoLoader DynaLoader Config DB VMS
293 strict vars FileHandle Exporter Carp)) {
294 $exclude{$pack."::"} = 1;
295 }
296 no strict qw(vars refs);
297 walksymtable(\%{"main::"}, "xref", sub { !defined($exclude{$_[0]}) });
298}
299
300sub output {
301 return if $raw;
302 my ($file, $subname, $pack, $name, $ev, $perfile, $persubname,
303 $perpack, $pername, $perev);
304 foreach $file (sort(keys(%table))) {
305 $perfile = $table{$file};
306 print "File $file\n";
307 foreach $subname (sort(keys(%$perfile))) {
308 $persubname = $perfile->{$subname};
309 print " Subroutine $subname\n";
310 foreach $pack (sort(keys(%$persubname))) {
311 $perpack = $persubname->{$pack};
312 print " Package $pack\n";
313 foreach $name (sort(keys(%$perpack))) {
314 $pername = $perpack->{$name};
315 my @lines;
316 foreach $ev (qw(intro formdef subdef meth subused used)) {
317 $perev = $pername->{$ev};
318 if (defined($perev) && @$perev) {
319 my $code = $code{$ev};
320 push(@lines, map("$code$_", @$perev));
321 }
322 }
323 printf " %-16s %s\n", $name, join(", ", @lines);
324 }
325 }
326 }
327 }
328}
329
330sub compile {
331 my @options = @_;
332 my ($option, $opt, $arg);
333 OPTION:
334 while ($option = shift @options) {
335 if ($option =~ /^-(.)(.*)/) {
336 $opt = $1;
337 $arg = $2;
338 } else {
339 unshift @options, $option;
340 last OPTION;
341 }
342 if ($opt eq "-" && $arg eq "-") {
343 shift @options;
344 last OPTION;
345 } elsif ($opt eq "o") {
346 $arg ||= shift @options;
347 open(STDOUT, ">$arg") or return "$arg: $!\n";
348 } elsif ($opt eq "d") {
349 $nodefs = 1;
350 } elsif ($opt eq "r") {
351 $raw = 1;
352 } elsif ($opt eq "D") {
353 $arg ||= shift @options;
354 foreach $arg (split(//, $arg)) {
355 if ($arg eq "o") {
356 B->debug(1);
357 } elsif ($arg eq "O") {
358 $debug_op = 1;
359 } elsif ($arg eq "t") {
360 $debug_top = 1;
361 }
362 }
363 }
364 }
365 if (@options) {
366 return sub {
367 my $objname;
368 xref_definitions();
369 foreach $objname (@options) {
370 $objname = "main::$objname" unless $objname =~ /::/;
371 eval "xref_object(\\&$objname)";
372 die "xref_object(\\&$objname) failed: $@" if $@;
373 }
374 output();
375 }
376 } else {
377 return sub {
378 xref_definitions();
379 xref_main();
380 output();
381 }
382 }
383}
384
3851;