File Coverage

blib/lib/Data/Dmp.pm
Criterion Covered Total %
statement 91 104 87.5
branch 45 52 86.5
condition 17 21 80.9
subroutine 11 13 84.6
pod 4 4 100.0
total 168 194 86.6


line stmt bran cond sub pod time code
1             ## no critic: Modules::ProhibitAutomaticExportation
2              
3             package Data::Dmp;
4              
5             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
6             our $DATE = '2020-04-07'; # DATE
7             our $DIST = 'Data-Dmp'; # DIST
8             our $VERSION = '0.240'; # VERSION
9              
10 1     1   591 use 5.010001;
  1         8  
11 1     1   5 use strict;
  1         1  
  1         19  
12 1     1   4 use warnings;
  1         2  
  1         44  
13              
14 1     1   6 use Scalar::Util qw(looks_like_number blessed reftype refaddr);
  1         2  
  1         1945  
15              
16             require Exporter;
17             our @ISA = qw(Exporter);
18             our @EXPORT = qw(dd dmp);
19             our @EXPORT_OK = qw(dd_ellipsis dmp_ellipsis);
20              
21             # for when dealing with circular refs
22             our %_seen_refaddrs;
23             our %_subscripts;
24             our @_fixups;
25              
26             our $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS = 70;
27             our $OPT_PERL_VERSION = "5.010";
28             our $OPT_REMOVE_PRAGMAS = 0;
29             our $OPT_DEPARSE = 1;
30             our $OPT_STRINGIFY_NUMBERS = 0;
31              
32             # BEGIN COPY PASTE FROM Data::Dump
33             my %esc = (
34             "\a" => "\\a",
35             "\b" => "\\b",
36             "\t" => "\\t",
37             "\n" => "\\n",
38             "\f" => "\\f",
39             "\r" => "\\r",
40             "\e" => "\\e",
41             );
42              
43             # put a string value in double quotes
44             sub _double_quote {
45 16     16   32 local($_) = $_[0];
46              
47             # If there are many '"' we might want to use qq() instead
48 16         35 s/([\\\"\@\$])/\\$1/g;
49 16 100       71 return qq("$_") unless /[^\040-\176]/; # fast exit
50              
51 1         10 s/([\a\b\t\n\f\r\e])/$esc{$1}/g;
52              
53             # no need for 3 digits in escape for these
54 1         3 s/([\0-\037])(?!\d)/sprintf('\\%o',ord($1))/eg;
  0         0  
55              
56 1         3 s/([\0-\037\177-\377])/sprintf('\\x%02X',ord($1))/eg;
  0         0  
57 1         3 s/([^\040-\176])/sprintf('\\x{%X}',ord($1))/eg;
  0         0  
58              
59 1         4 return qq("$_");
60             }
61             # END COPY PASTE FROM Data::Dump
62              
63             sub _dump_code {
64 4     4   9 my $code = shift;
65              
66 4         6 state $deparse = do {
67 1         8 require B::Deparse;
68 1         65 B::Deparse->new("-l"); # -i option doesn't have any effect?
69             };
70              
71 4         4161 my $res = $deparse->coderef2text($code);
72              
73 4         60 my ($res_before_first_line, $res_after_first_line) =
74             $res =~ /(.+?)^(#line .+)/ms;
75              
76 4 100       18 if ($OPT_REMOVE_PRAGMAS) {
    50          
77 3         7 $res_before_first_line = "{";
78             } elsif ($OPT_PERL_VERSION < 5.016) {
79             # older perls' feature.pm doesn't yet support q{no feature ':all';}
80             # so we replace it with q{no feature}.
81 1         8 $res_before_first_line =~ s/no feature ':all';/no feature;/m;
82             }
83 4         30 $res_after_first_line =~ s/^#line .+//gm;
84              
85 4         12 $res = "sub" . $res_before_first_line . $res_after_first_line;
86 4         22 $res =~ s/^\s+//gm;
87 4         21 $res =~ s/\n+//g;
88 4         15 $res =~ s/;\}\z/}/;
89 4         11 $res;
90             }
91              
92             sub _quote_key {
93 9 100 100 9   54 $_[0] =~ /\A-?[A-Za-z_][A-Za-z0-9_]*\z/ ||
94             $_[0] =~ /\A-?[1-9][0-9]{0,8}\z/ ? $_[0] : _double_quote($_[0]);
95             }
96              
97             sub _dump {
98 47     47   92 my ($val, $subscript) = @_;
99              
100 47         81 my $ref = ref($val);
101 47 100       91 if ($ref eq '') {
102 26 100 100     188 if (!defined($val)) {
    100 100        
      100        
103 1         3 return "undef";
104             } elsif (looks_like_number($val) && !$OPT_STRINGIFY_NUMBERS &&
105             # perl does several normalizations to number literal, e.g.
106             # "+1" becomes 1, 0123 is octal literal, etc. make sure we
107             # only leave out quote when the number is not normalized
108             $val eq $val+0 &&
109             # perl also doesn't recognize Inf and NaN as numeric
110             # literals (ref: perldata) so these unquoted literals will
111             # choke under 'use strict "subs"
112             $val !~ /\A-?(?:inf(?:inity)?|nan)\z/i
113             ) {
114 14         34 return $val;
115             } else {
116 11         25 return _double_quote($val);
117             }
118             }
119 21         46 my $refaddr = refaddr($val);
120 21   100     107 $_subscripts{$refaddr} //= $subscript;
121 21 100       52 if ($_seen_refaddrs{$refaddr}++) {
122             push @_fixups, "\$a->$subscript=\$a",
123 3 50       12 ($_subscripts{$refaddr} ? "->$_subscripts{$refaddr}" : ""), ";";
124 3         7 return "'fix'";
125             }
126              
127 18         26 my $class;
128              
129 18 100 66     60 if ($ref eq 'Regexp' || $ref eq 'REGEXP') {
130 1         626 require Regexp::Stringify;
131 1         760 return Regexp::Stringify::stringify_regexp(
132             regexp=>$val, with_qr=>1, plver=>$OPT_PERL_VERSION);
133             }
134              
135 17 100       40 if (blessed $val) {
136 1         3 $class = $ref;
137 1         3 $ref = reftype($val);
138             }
139              
140 17         21 my $res;
141 17 100       64 if ($ref eq 'ARRAY') {
    100          
    100          
    100          
    50          
142 4         6 $res = "[";
143 4         6 my $i = 0;
144 4         8 for (@$val) {
145 8 100       15 $res .= "," if $i;
146 8         20 $res .= _dump($_, "$subscript\[$i]");
147 8         18 $i++;
148             }
149 4         7 $res .= "]";
150             } elsif ($ref eq 'HASH') {
151 5         9 $res = "{";
152 5         8 my $i = 0;
153 5         30 for (sort keys %$val) {
154 9 100       23 $res .= "," if $i++;
155 9         14 my $k = _quote_key($_);
156 9         33 my $v = _dump($val->{$_}, "$subscript\{$k}");
157 9         24 $res .= "$k=>$v";
158             }
159 5         10 $res .= "}";
160             } elsif ($ref eq 'SCALAR') {
161 2         13 $res = "\\"._dump($$val, $subscript);
162             } elsif ($ref eq 'REF') {
163 1         4 $res = "\\"._dump($$val, $subscript);
164             } elsif ($ref eq 'CODE') {
165 5 100       13 $res = $OPT_DEPARSE ? _dump_code($val) : 'sub{"DUMMY"}';
166             } else {
167 0         0 die "Sorry, I can't dump $val (ref=$ref) yet";
168             }
169              
170 17 100       39 $res = "bless($res,"._double_quote($class).")" if defined($class);
171 17         34 $res;
172             }
173              
174             our $_is_dd;
175             our $_is_ellipsis;
176             sub _dd_or_dmp {
177 27     27   52 local %_seen_refaddrs;
178 27         48 local %_subscripts;
179 27         37 local @_fixups;
180              
181 27         35 my $res;
182 27 50       78 if (@_ > 1) {
183 0         0 $res = "(" . join(",", map {_dump($_, '')} @_) . ")";
  0         0  
184             } else {
185 27         61 $res = _dump($_[0], '');
186             }
187 27 100       115 if (@_fixups) {
188 2         9 $res = "do{my\$a=$res;" . join("", @_fixups) . "\$a}";
189             }
190              
191 27 100       51 if ($_is_ellipsis) {
192 2 100       8 $res = substr($res, 0, $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS) . '...'
193             if length($res) > $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS;
194             }
195              
196 27 50       57 if ($_is_dd) {
197 0         0 say $res;
198 0 0 0     0 return wantarray() || @_ > 1 ? @_ : $_[0];
199             } else {
200 27         147 return $res;
201             }
202             }
203              
204 0     0 1 0 sub dd { local $_is_dd=1; _dd_or_dmp(@_) } # goto &sub doesn't work with local
  0         0  
205 25     25 1 3888 sub dmp { goto &_dd_or_dmp }
206              
207 0     0 1 0 sub dd_ellipsis { local $_is_dd=1; local $_is_ellipsis=1; _dd_or_dmp(@_) }
  0         0  
  0         0  
208 2     2 1 730 sub dmp_ellipsis { local $_is_ellipsis=1; _dd_or_dmp(@_) }
  2         5  
209              
210             1;
211             # ABSTRACT: Dump Perl data structures as Perl code
212              
213             __END__