This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Merge branch 'vlb' into blead
[perl5.git] / lib / dumpvar.pl
CommitLineData
0d0268b0 1require 5.014; # For more reliable $@ after eval
d338d6fe 2package dumpvar;
3
4# Needed for PrettyPrinter only:
5
6# require 5.001; # Well, it coredumps anyway undef DB in 5.000 (not now)
7
8# translate control chars to ^X - Randal Schwartz
9# Modifications to print types by Peter Gordon v1.0
10
11# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
12
13# Won't dump symbol tables and contents of debugged files by default
14
15$winsize = 80 unless defined $winsize;
16
c9674c0f
KW
17sub ASCII { return ord('A') == 65; }
18
d338d6fe 19
20# Defaults
21
22# $globPrint = 1;
23$printUndef = 1 unless defined $printUndef;
54d04a52 24$tick = "auto" unless defined $tick;
d338d6fe 25$unctrl = 'quote' unless defined $unctrl;
54d04a52 26$subdump = 1;
22fae026 27$dumpReused = 0 unless defined $dumpReused;
ee239bfe 28$bareStringify = 1 unless defined $bareStringify;
d338d6fe 29
c9674c0f
KW
30my $APC = chr utf8::unicode_to_native(0x9F);
31my $backslash_c_question = (ASCII) ? '\177' : $APC;
32
d338d6fe 33sub main::dumpValue {
34 local %address;
b2391ea8 35 local $^W=0;
d338d6fe 36 (print "undef\n"), return unless defined $_[0];
37 (print &stringify($_[0]), "\n"), return unless ref $_[0];
f338af47
IZ
38 push @_, -1 if @_ == 1;
39 dumpvar::unwrap($_[0], 0, $_[1]);
d338d6fe 40}
41
42# This one is good for variable names:
43
44sub unctrl {
6cee328e 45 for (my($dummy) = shift) {
d338d6fe 46 local($v) ;
47
48 return \$_ if ref \$_ eq "GLOB";
c9674c0f
KW
49 s/([\000-\037])/ '^' . chr(utf8::unicode_to_native(ord($1)^64))/eg;
50 s/ $backslash_c_question /^?/xg;
6cee328e
FC
51 return $_;
52 }
d338d6fe 53}
54
f8543d02
A
55sub uniescape {
56 join("",
57 map { $_ > 255 ? sprintf("\\x{%04X}", $_) : chr($_) }
c9674c0f 58 unpack("W*", $_[0]));
f8543d02
A
59}
60
d338d6fe 61sub stringify {
0d0268b0
RS
62 my $string;
63 if (eval { $string = _stringify(@_); 1 }) {
64 return $string;
65 }
66
67 return "<< value could not be dumped: $@ >>";
68}
69
70sub _stringify {
6cee328e
FC
71 (my $__, local $noticks) = @_;
72 for ($__) {
d338d6fe 73 local($v) ;
54d04a52 74 my $tick = $tick;
d338d6fe 75
76 return 'undef' unless defined $_ or not $printUndef;
77 return $_ . "" if ref \$_ eq 'GLOB';
ee239bfe
IZ
78 $_ = &{'overload::StrVal'}($_)
79 if $bareStringify and ref $_
475342a6 80 and %overload:: and defined &{'overload::StrVal'};
ee239bfe 81
54d04a52 82 if ($tick eq 'auto') {
c9674c0f
KW
83 if (/[^[:^cntrl:]\n]/u) { # All controls but \n get '"'
84 $tick = '"';
85 } else {
86 $tick = "'";
87 }
54d04a52 88 }
d338d6fe 89 if ($tick eq "'") {
90 s/([\'\\])/\\$1/g;
91 } elsif ($unctrl eq 'unctrl') {
92 s/([\"\\])/\\$1/g ;
c9674c0f 93 $_ = &unctrl($_);
f8543d02 94 # uniescape?
c9674c0f 95 s/([[:^ascii:]])/'\\0x'.sprintf('%2X',ord($1))/eg
d338d6fe 96 if $quoteHighBit;
97 } elsif ($unctrl eq 'quote') {
98 s/([\"\\\$\@])/\\$1/g if $tick eq '"';
c9674c0f
KW
99 s/\e/\\e/g;
100 s/([\000-\037$backslash_c_question])/'\\c'._escaped_ord($1)/eg;
d338d6fe 101 }
f8543d02 102 $_ = uniescape($_);
c9674c0f 103 s/([[:^ascii:]])/'\\'.sprintf('%3o',ord($1))/eg if $quoteHighBit;
6cee328e 104 return ($noticks || /^\d+(\.\d*)?\Z/)
d338d6fe 105 ? $_
106 : $tick . $_ . $tick;
6cee328e 107 }
d338d6fe 108}
109
bd6f3d36
MS
110# Ensure a resulting \ is escaped to be \\
111sub _escaped_ord {
112 my $chr = shift;
c9674c0f
KW
113 if ($chr eq $backslash_c_question) {
114 $chr = '?';
115 }
116 else {
117 $chr = chr(utf8::unicode_to_native(ord($chr)^64));
118 $chr =~ s{\\}{\\\\}g;
119 }
bd6f3d36
MS
120 return $chr;
121}
122
d338d6fe 123sub ShortArray {
124 my $tArrayDepth = $#{$_[0]} ;
125 $tArrayDepth = $#{$_[0]} < $arrayDepth-1 ? $#{$_[0]} : $arrayDepth-1
126 unless $arrayDepth eq '' ;
127 my $shortmore = "";
128 $shortmore = " ..." if $tArrayDepth < $#{$_[0]} ;
129 if (!grep(ref $_, @{$_[0]})) {
130 $short = "0..$#{$_[0]} '" .
131 join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
132 return $short if length $short <= $compactDump;
133 }
134 undef;
135}
136
137sub DumpElem {
138 my $short = &stringify($_[0], ref $_[0]);
139 if ($veryCompact && ref $_[0]
140 && (ref $_[0] eq 'ARRAY' and !grep(ref $_, @{$_[0]}) )) {
141 my $end = "0..$#{$v} '" .
142 join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
143 } elsif ($veryCompact && ref $_[0]
144 && (ref $_[0] eq 'HASH') and !grep(ref $_, values %{$_[0]})) {
145 my $end = 1;
146 $short = $sp . "0..$#{$v} '" .
147 join("' '", @{$v}[0..$tArrayDepth]) . "'$shortmore";
148 } else {
149 print "$short\n";
9bcb75ad 150 unwrap($_[0],$_[1],$_[2]) if ref $_[0];
d338d6fe 151 }
152}
153
154sub unwrap {
155 return if $DB::signal;
156 local($v) = shift ;
157 local($s) = shift ; # extra no of spaces
d03c2a1b
MJD
158 local($m) = shift ; # maximum recursion depth
159 return if $m == 0;
ee239bfe 160 local(%v,@v,$sp,$value,$key,@sortKeys,$more,$shortmore,$short) ;
d338d6fe 161 local($tHashDepth,$tArrayDepth) ;
162
163 $sp = " " x $s ;
164 $s += 3 ;
165
0d0268b0 166 eval {
d338d6fe 167 # Check for reused addresses
168 if (ref $v) {
ee239bfe
IZ
169 my $val = $v;
170 $val = &{'overload::StrVal'}($v)
475342a6 171 if %overload:: and defined &{'overload::StrVal'};
9bcb75ad
JM
172 # Match type and address.
173 # Unblessed references will look like TYPE(0x...)
174 # Blessed references will look like Class=TYPE(0x...)
6b1b4e01 175 $val =~ s/^.*=//; # suppress the Class part, just keep TYPE(0x...)
9bcb75ad
JM
176 ($item_type, $address) =
177 $val =~ /([^\(]+) # Keep stuff that's
178 # not an open paren
179 \( # Skip open paren
180 (0x[0-9a-f]+) # Save the address
181 \) # Skip close paren
182 $/x; # Should be at end now
183
22fae026 184 if (!$dumpReused && defined $address) {
d338d6fe 185 $address{$address}++ ;
186 if ( $address{$address} > 1 ) {
187 print "${sp}-> REUSED_ADDRESS\n" ;
188 return ;
189 }
190 }
191 } elsif (ref \$v eq 'GLOB') {
9bcb75ad 192 # This is a raw glob. Special handling for that.
d338d6fe 193 $address = "$v" . ""; # To avoid a bug with globs
194 $address{$address}++ ;
195 if ( $address{$address} > 1 ) {
196 print "${sp}*DUMPED_GLOB*\n" ;
197 return ;
198 }
199 }
200
7894fbab 201 if (ref $v eq 'Regexp') {
9bcb75ad 202 # Reformat the regexp to look the standard way.
7894fbab
GS
203 my $re = "$v";
204 $re =~ s,/,\\/,g;
205 print "$sp-> qr/$re/\n";
206 return;
207 }
208
9bcb75ad
JM
209 if ( $item_type eq 'HASH' ) {
210 # Hash ref or hash-based object.
211 my @sortKeys = sort keys(%$v) ;
d338d6fe 212 undef $more ;
213 $tHashDepth = $#sortKeys ;
214 $tHashDepth = $#sortKeys < $hashDepth-1 ? $#sortKeys : $hashDepth-1
215 unless $hashDepth eq '' ;
216 $more = "....\n" if $tHashDepth < $#sortKeys ;
217 $shortmore = "";
218 $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
219 $#sortKeys = $tHashDepth ;
220 if ($compactDump && !grep(ref $_, values %{$v})) {
221 #$short = $sp .
222 # (join ', ',
223# Next row core dumps during require from DB on 5.000, even with map {"_"}
224 # map {&stringify($_) . " => " . &stringify($v->{$_})}
225 # @sortKeys) . "'$shortmore";
226 $short = $sp;
227 my @keys;
228 for (@sortKeys) {
229 push @keys, &stringify($_) . " => " . &stringify($v->{$_});
230 }
231 $short .= join ', ', @keys;
232 $short .= $shortmore;
233 (print "$short\n"), return if length $short <= $compactDump;
234 }
235 for $key (@sortKeys) {
236 return if $DB::signal;
237 $value = $ {$v}{$key} ;
238 print "$sp", &stringify($key), " => ";
d03c2a1b 239 DumpElem $value, $s, $m-1;
d338d6fe 240 }
241 print "$sp empty hash\n" unless @sortKeys;
242 print "$sp$more" if defined $more ;
9bcb75ad
JM
243 } elsif ( $item_type eq 'ARRAY' ) {
244 # Array ref or array-based object. Also: undef.
245 # See how big the array is.
d338d6fe 246 $tArrayDepth = $#{$v} ;
247 undef $more ;
9bcb75ad 248 # Bigger than the max?
d338d6fe 249 $tArrayDepth = $#{$v} < $arrayDepth-1 ? $#{$v} : $arrayDepth-1
600d99fa 250 if defined $arrayDepth && $arrayDepth ne '';
9bcb75ad 251 # Yep. Don't show it all.
d338d6fe 252 $more = "....\n" if $tArrayDepth < $#{$v} ;
253 $shortmore = "";
254 $shortmore = " ..." if $tArrayDepth < $#{$v} ;
9bcb75ad 255
d338d6fe 256 if ($compactDump && !grep(ref $_, @{$v})) {
257 if ($#$v >= 0) {
54d04a52
IZ
258 $short = $sp . "0..$#{$v} " .
259 join(" ",
859172fe 260 map {exists $v->[$_] ? stringify $v->[$_] : "empty"} (0..$tArrayDepth)
d9182636 261 ) . "$shortmore";
d338d6fe 262 } else {
263 $short = $sp . "empty array";
264 }
265 (print "$short\n"), return if length $short <= $compactDump;
266 }
267 #if ($compactDump && $short = ShortArray($v)) {
268 # print "$short\n";
269 # return;
270 #}
859172fe 271 for $num (0 .. $tArrayDepth) {
d338d6fe 272 return if $DB::signal;
273 print "$sp$num ";
d9182636 274 if (exists $v->[$num]) {
9bcb75ad
JM
275 if (defined $v->[$num]) {
276 DumpElem $v->[$num], $s, $m-1;
277 }
278 else {
279 print "undef\n";
280 }
d9182636
GS
281 } else {
282 print "empty slot\n";
283 }
d338d6fe 284 }
285 print "$sp empty array\n" unless @$v;
286 print "$sp$more" if defined $more ;
9bcb75ad
JM
287 } elsif ( $item_type eq 'SCALAR' ) {
288 unless (defined $$v) {
289 print "$sp-> undef\n";
290 return;
291 }
d338d6fe 292 print "$sp-> ";
d03c2a1b 293 DumpElem $$v, $s, $m-1;
9bcb75ad
JM
294 } elsif ( $item_type eq 'REF' ) {
295 print "$sp-> $$v\n";
296 return unless defined $$v;
297 unwrap($$v, $s+3, $m-1);
298 } elsif ( $item_type eq 'CODE' ) {
299 # Code object or reference.
54d04a52
IZ
300 print "$sp-> ";
301 dumpsub (0, $v);
9bcb75ad
JM
302 } elsif ( $item_type eq 'GLOB' ) {
303 # Glob object or reference.
d338d6fe 304 print "$sp-> ",&stringify($$v,1),"\n";
305 if ($globPrint) {
306 $s += 3;
3a4b996c 307 dumpglob($s, "{$$v}", $$v, 1, $m-1);
bef1d284 308 } elsif (defined ($fileno = eval {fileno($v)})) {
d338d6fe 309 print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" );
310 }
311 } elsif (ref \$v eq 'GLOB') {
9bcb75ad 312 # Raw glob (again?)
d338d6fe 313 if ($globPrint) {
3a4b996c 314 dumpglob($s, "{$v}", $v, 1, $m-1) if $globPrint;
bef1d284 315 } elsif (defined ($fileno = eval {fileno(\$v)})) {
d338d6fe 316 print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" );
317 }
318 }
0d0268b0
RS
319 };
320 if ($@) {
321 print( (' ' x $s) . "<< value could not be dumped: $@ >>\n");
322 }
323
324 return;
d338d6fe 325}
326
a7b657ee
PS
327sub matchlex {
328 (my $var = $_[0]) =~ s/.//;
329 $var eq $_[1] or
330 ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
331 ($1 eq '!') ^ (eval { $var =~ /$2$3/ });
332}
333
d338d6fe 334sub matchvar {
335 $_[0] eq $_[1] or
b2391ea8 336 ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
337 ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
d338d6fe 338}
339
340sub compactDump {
341 $compactDump = shift if @_;
342 $compactDump = 6*80-1 if $compactDump and $compactDump < 2;
343 $compactDump;
344}
345
346sub veryCompact {
347 $veryCompact = shift if @_;
348 compactDump(1) if !$compactDump and $veryCompact;
349 $veryCompact;
350}
351
352sub unctrlSet {
353 if (@_) {
354 my $in = shift;
355 if ($in eq 'unctrl' or $in eq 'quote') {
356 $unctrl = $in;
357 } else {
1f874cb6 358 print "Unknown value for 'unctrl'.\n";
d338d6fe 359 }
360 }
361 $unctrl;
362}
363
364sub quote {
365 if (@_ and $_[0] eq '"') {
366 $tick = '"';
367 $unctrl = 'quote';
54d04a52
IZ
368 } elsif (@_ and $_[0] eq 'auto') {
369 $tick = 'auto';
370 $unctrl = 'quote';
d338d6fe 371 } elsif (@_) { # Need to set
372 $tick = "'";
373 $unctrl = 'unctrl';
374 }
375 $tick;
376}
377
378sub dumpglob {
379 return if $DB::signal;
3a4b996c 380 my ($off,$key, $val, $all, $m) = @_;
d338d6fe 381 local(*entry) = $val;
382 my $fileno;
54d04a52 383 if (($key !~ /^_</ or $dumpDBFiles) and defined $entry) {
d338d6fe 384 print( (' ' x $off) . "\$", &unctrl($key), " = " );
3a4b996c 385 DumpElem $entry, 3+$off, $m;
d338d6fe 386 }
475342a6 387 if (($key !~ /^_</ or $dumpDBFiles) and @entry) {
d338d6fe 388 print( (' ' x $off) . "\@$key = (\n" );
3a4b996c 389 unwrap(\@entry,3+$off,$m) ;
d338d6fe 390 print( (' ' x $off) . ")\n" );
391 }
475342a6 392 if ($key ne "main::" && $key ne "DB::" && %entry
d338d6fe 393 && ($dumpPackages or $key !~ /::$/)
54d04a52 394 && ($key !~ /^_</ or $dumpDBFiles)
d338d6fe 395 && !($package eq "dumpvar" and $key eq "stab")) {
396 print( (' ' x $off) . "\%$key = (\n" );
3a4b996c 397 unwrap(\%entry,3+$off,$m) ;
d338d6fe 398 print( (' ' x $off) . ")\n" );
399 }
bef1d284 400 if (defined ($fileno = eval{fileno(*entry)})) {
d338d6fe 401 print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
402 }
403 if ($all) {
404 if (defined &entry) {
54d04a52 405 dumpsub($off, $key);
d338d6fe 406 }
407 }
408}
409
a7b657ee
PS
410sub dumplex {
411 return if $DB::signal;
412 my ($key, $val, $m, @vars) = @_;
413 return if @vars && !grep( matchlex($key, $_), @vars );
414 local %address;
415 my $off = 0; # It reads better this way
416 my $fileno;
417 if (UNIVERSAL::isa($val,'ARRAY')) {
418 print( (' ' x $off) . "$key = (\n" );
419 unwrap($val,3+$off,$m) ;
420 print( (' ' x $off) . ")\n" );
421 }
422 elsif (UNIVERSAL::isa($val,'HASH')) {
423 print( (' ' x $off) . "$key = (\n" );
424 unwrap($val,3+$off,$m) ;
425 print( (' ' x $off) . ")\n" );
426 }
427 elsif (UNIVERSAL::isa($val,'IO')) {
428 print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
429 }
430 # No lexical subroutines yet...
431 # elsif (UNIVERSAL::isa($val,'CODE')) {
432 # dumpsub($off, $$val);
433 # }
434 else {
435 print( (' ' x $off) . &unctrl($key), " = " );
436 DumpElem $$val, 3+$off, $m;
437 }
438}
439
83ee9e09
GS
440sub CvGV_name_or_bust {
441 my $in = shift;
442 return if $skipCvGV; # Backdoor to avoid problems if XS broken...
443 $in = \&$in; # Hard reference...
444 eval {require Devel::Peek; 1} or return;
445 my $gv = Devel::Peek::CvGV($in) or return;
446 *$gv{PACKAGE} . '::' . *$gv{NAME};
447}
448
54d04a52
IZ
449sub dumpsub {
450 my ($off,$sub) = @_;
83ee9e09
GS
451 my $ini = $sub;
452 my $s;
54d04a52 453 $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
83ee9e09
GS
454 my $subref = defined $1 ? \&$sub : \&$ini;
455 my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s})
456 || (($s = CvGV_name_or_bust($subref)) && $DB::sub{$s})
457 || ($subdump && ($s = findsubs("$subref")) && $DB::sub{$s});
54d04a52 458 $place = '???' unless defined $place;
83ee9e09
GS
459 $s = $sub unless defined $s;
460 print( (' ' x $off) . "&$s in $place\n" );
54d04a52
IZ
461}
462
463sub findsubs {
475342a6 464 return undef unless %DB::sub;
54d04a52
IZ
465 my ($addr, $name, $loc);
466 while (($name, $loc) = each %DB::sub) {
467 $addr = \&$name;
468 $subs{"$addr"} = $name;
469 }
470 $subdump = 0;
471 $subs{ shift() };
472}
473
d338d6fe 474sub main::dumpvar {
3a4b996c 475 my ($package,$m,@vars) = @_;
b2391ea8 476 local(%address,$key,$val,$^W);
d338d6fe 477 $package .= "::" unless $package =~ /::$/;
478 *stab = *{"main::"};
479 while ($package =~ /(\w+?::)/g){
480 *stab = $ {stab}{$1};
481 }
482 local $TotalStrings = 0;
483 local $Strings = 0;
484 local $CompleteTotal = 0;
485 while (($key,$val) = each(%stab)) {
486 return if $DB::signal;
487 next if @vars && !grep( matchvar($key, $_), @vars );
488 if ($usageOnly) {
4c82ae22
GS
489 globUsage(\$val, $key)
490 if ($package ne 'dumpvar' or $key ne 'stab')
491 and ref(\$val) eq 'GLOB';
d338d6fe 492 } else {
3a4b996c 493 dumpglob(0,$key, $val, 0, $m);
d338d6fe 494 }
495 }
496 if ($usageOnly) {
497 print "String space: $TotalStrings bytes in $Strings strings.\n";
498 $CompleteTotal += $TotalStrings;
499 print "Grand total = $CompleteTotal bytes (1 level deep) + overhead.\n";
500 }
501}
502
503sub scalarUsage {
504 my $size = length($_[0]);
505 $TotalStrings += $size;
506 $Strings++;
507 $size;
508}
509
510sub arrayUsage { # array ref, name
511 my $size = 0;
512 map {$size += scalarUsage($_)} @{$_[0]};
513 my $len = @{$_[0]};
514 print "\@$_[1] = $len item", ($len > 1 ? "s" : ""),
515 " (data: $size bytes)\n"
516 if defined $_[1];
517 $CompleteTotal += $size;
518 $size;
519}
520
521sub hashUsage { # hash ref, name
522 my @keys = keys %{$_[0]};
523 my @values = values %{$_[0]};
524 my $keys = arrayUsage \@keys;
525 my $values = arrayUsage \@values;
526 my $len = @keys;
527 my $total = $keys + $values;
528 print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
529 " (keys: $keys; values: $values; total: $total bytes)\n"
530 if defined $_[1];
531 $total;
532}
533
534sub globUsage { # glob ref, name
535 local *name = *{$_[0]};
536 $total = 0;
537 $total += scalarUsage $name if defined $name;
475342a6
GS
538 $total += arrayUsage \@name, $_[1] if @name;
539 $total += hashUsage \%name, $_[1] if %name and $_[1] ne "main::"
d338d6fe 540 and $_[1] ne "DB::"; #and !($package eq "dumpvar" and $key eq "stab"));
541 $total;
542}
543
544sub packageUsage {
545 my ($package,@vars) = @_;
546 $package .= "::" unless $package =~ /::$/;
547 local *stab = *{"main::"};
548 while ($package =~ /(\w+?::)/g){
549 *stab = $ {stab}{$1};
550 }
551 local $TotalStrings = 0;
552 local $CompleteTotal = 0;
553 my ($key,$val);
554 while (($key,$val) = each(%stab)) {
555 next if @vars && !grep($key eq $_,@vars);
556 globUsage \$val, $key unless $package eq 'dumpvar' and $key eq 'stab';
557 }
558 print "String space: $TotalStrings.\n";
559 $CompleteTotal += $TotalStrings;
560 print "\nGrand total = $CompleteTotal bytes\n";
561}
562
5631;
564