Skip to content

Commit b5048e7

Browse files
committed
Data::Dumper: the XS implementation now supports Deparse
This will provide a significant performance enhancement for callers that use deparsing (including Data::Dumper::Concise). There are no longer any configuration settings or (when run on Perl 5.21.10 or later) platforms that force use of the pure-Perl implementation.
1 parent 942cf64 commit b5048e7

File tree

4 files changed

+107
-26
lines changed

4 files changed

+107
-26
lines changed

dist/Data-Dumper/Changes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ Changes - public release history for Data::Dumper
66

77
=over 8
88

9+
=item NEXT
10+
11+
The XS implementation now handles the C<Deparse> option, so using it no
12+
longer forces use of the pure-Perl version.
13+
914
=item 2.161 (Jul 11 2016)
1015

1116
Perl 5.12 fix/workaround until fixed PPPort release.

dist/Data-Dumper/Dumper.pm

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ sub DESTROY {}
227227
sub Dump {
228228
return &Dumpxs
229229
unless $Data::Dumper::Useperl || (ref($_[0]) && $_[0]->{useperl})
230-
|| $Data::Dumper::Deparse || (ref($_[0]) && $_[0]->{deparse})
231230

232231
# Use pure perl version on earlier releases on EBCDIC platforms
233232
|| (! $IS_ASCII && $] lt 5.021_010);
@@ -1212,9 +1211,10 @@ $Data::Dumper::Deparse I<or> $I<OBJ>->Deparse(I<[NEWVAL]>)
12121211
12131212
Can be set to a boolean value to control whether code references are
12141213
turned into perl source code. If set to a true value, C<B::Deparse>
1215-
will be used to get the source of the code reference. Using this option
1216-
will force using the Perl implementation of the dumper, since the fast
1217-
XSUB implementation doesn't support it.
1214+
will be used to get the source of the code reference. In older versions,
1215+
using this option imposed a significant performance penalty when dumping
1216+
parts of a data structure other than code references, but that is no
1217+
longer the case.
12181218
12191219
Caution : use this option only if you know that your coderefs will be
12201220
properly reconstructed by C<B::Deparse>.
@@ -1435,15 +1435,9 @@ the C<Deparse> flag), an anonymous subroutine that
14351435
contains the string '"DUMMY"' will be inserted in its place, and a warning
14361436
will be printed if C<Purity> is set. You can C<eval> the result, but bear
14371437
in mind that the anonymous sub that gets created is just a placeholder.
1438-
Someday, perl will have a switch to cache-on-demand the string
1439-
representation of a compiled piece of code, I hope. If you have prior
1440-
knowledge of all the code refs that your data structures are likely
1441-
to have, you can use the C<Seen> method to pre-seed the internal reference
1442-
table and make the dumped output point to them, instead. See L</EXAMPLES>
1443-
above.
1444-
1445-
The C<Deparse> flag makes Dump() run slower, since the XSUB
1446-
implementation does not support it.
1438+
Even using the C<Deparse> flag will in some cases produce results that
1439+
behave differently after being passed to C<eval>; see the documentation
1440+
for L<B::Deparse>.
14471441
14481442
SCALAR objects have the weirdest looking C<bless> workaround.
14491443

dist/Data-Dumper/Dumper.xs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ typedef struct {
6363
I32 useqq;
6464
int use_sparse_seen_hash;
6565
int trailingcomma;
66+
int deparse;
6667
} Style;
6768

6869
static STRLEN num_q (const char *s, STRLEN slen);
@@ -505,6 +506,51 @@ sv_x(pTHX_ SV *sv, const char *str, STRLEN len, I32 n)
505506
return sv;
506507
}
507508

509+
static SV *
510+
deparsed_output(pTHX_ SV *val)
511+
{
512+
SV *text;
513+
int n;
514+
dSP;
515+
516+
/* This is passed to load_module(), which decrements its ref count and
517+
* modifies it (so we also can't reuse it below) */
518+
SV *pkg = newSVpvs("B::Deparse");
519+
520+
load_module(PERL_LOADMOD_NOIMPORT, pkg, 0);
521+
522+
SAVETMPS;
523+
524+
PUSHMARK(SP);
525+
mXPUSHs(newSVpvs("B::Deparse"));
526+
PUTBACK;
527+
528+
n = call_method("new", G_SCALAR);
529+
SPAGAIN;
530+
531+
if (n != 1) {
532+
croak("B::Deparse->new returned %d items, but expected exactly 1", n);
533+
}
534+
535+
PUSHMARK(SP - n);
536+
XPUSHs(val);
537+
PUTBACK;
538+
539+
n = call_method("coderef2text", G_SCALAR);
540+
SPAGAIN;
541+
542+
if (n != 1) {
543+
croak("$b_deparse->coderef2text returned %d items, but expected exactly 1", n);
544+
}
545+
546+
text = POPs;
547+
SvREFCNT_inc(text); /* the caller will mortalise this */
548+
549+
FREETMPS;
550+
551+
return text;
552+
}
553+
508554
/*
509555
* This ought to be split into smaller functions. (it is one long function since
510556
* it exactly parallels the perl version, which was one long thing for
@@ -1095,9 +1141,41 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
10951141
SvREFCNT_dec(totpad);
10961142
}
10971143
else if (realtype == SVt_PVCV) {
1098-
sv_catpvs(retval, "sub { \"DUMMY\" }");
1099-
if (style->purity)
1100-
warn("Encountered CODE ref, using dummy placeholder");
1144+
if (style->deparse) {
1145+
SV *deparsed = sv_2mortal(deparsed_output(aTHX_ val));
1146+
SV *fullpad = sv_2mortal(newSVsv(style->sep));
1147+
const char *p;
1148+
STRLEN plen;
1149+
I32 i;
1150+
1151+
sv_catsv(fullpad, style->pad);
1152+
sv_catsv(fullpad, apad);
1153+
for (i = 0; i < level; i++) {
1154+
sv_catsv(fullpad, style->xpad);
1155+
}
1156+
1157+
sv_catpvs(retval, "sub ");
1158+
p = SvPV(deparsed, plen);
1159+
while (plen > 0) {
1160+
const char *nl = (const char *) memchr(p, '\n', plen);
1161+
if (!nl) {
1162+
sv_catpvn(retval, p, plen);
1163+
break;
1164+
}
1165+
else {
1166+
size_t n = nl - p;
1167+
sv_catpvn(retval, p, n);
1168+
sv_catsv(retval, fullpad);
1169+
p += n + 1;
1170+
plen -= n + 1;
1171+
}
1172+
}
1173+
}
1174+
else {
1175+
sv_catpvs(retval, "sub { \"DUMMY\" }");
1176+
if (style->purity)
1177+
warn("Encountered CODE ref, using dummy placeholder");
1178+
}
11011179
}
11021180
else {
11031181
warn("cannot handle ref type %d", (int)realtype);
@@ -1452,6 +1530,8 @@ Data_Dumper_Dumpxs(href, ...)
14521530
style.quotekeys = SvTRUE(*svp);
14531531
if ((svp = hv_fetchs(hv, "trailingcomma", FALSE)))
14541532
style.trailingcomma = SvTRUE(*svp);
1533+
if ((svp = hv_fetchs(hv, "deparse", FALSE)))
1534+
style.deparse = SvTRUE(*svp);
14551535
if ((svp = hv_fetchs(hv, "bless", FALSE)))
14561536
style.bless = *svp;
14571537
if ((svp = hv_fetchs(hv, "maxdepth", FALSE)))

dist/Data-Dumper/t/deparse.t

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ BEGIN {
1515
use strict;
1616

1717
use Data::Dumper;
18-
use Test::More tests => 8;
18+
use Test::More tests => 16;
1919
use lib qw( ./t/lib );
2020
use Testing qw( _dumptostr );
2121

@@ -24,7 +24,9 @@ use Testing qw( _dumptostr );
2424

2525
note("\$Data::Dumper::Deparse and Deparse()");
2626

27-
{
27+
for my $useperl (0, 1) {
28+
local $Data::Dumper::Useperl = $useperl;
29+
2830
my ($obj, %dumps, $deparse, $starting);
2931
use strict;
3032
my $struct = { foo => "bar\nbaz", quux => sub { "fleem" } };
@@ -46,11 +48,11 @@ note("\$Data::Dumper::Deparse and Deparse()");
4648
$dumps{'objzero'} = _dumptostr($obj);
4749

4850
is($dumps{'noprev'}, $dumps{'dddzero'},
49-
"No previous setting and \$Data::Dumper::Deparse = 0 are equivalent");
51+
"No previous setting and \$Data::Dumper::Deparse = 0 are equivalent (useperl=$useperl)");
5052
is($dumps{'noprev'}, $dumps{'objempty'},
51-
"No previous setting and Deparse() are equivalent");
53+
"No previous setting and Deparse() are equivalent (useperl=$useperl)");
5254
is($dumps{'noprev'}, $dumps{'objzero'},
53-
"No previous setting and Deparse(0) are equivalent");
55+
"No previous setting and Deparse(0) are equivalent (useperl=$useperl)");
5456

5557
local $Data::Dumper::Deparse = 1;
5658
$obj = Data::Dumper->new( [ $struct ] );
@@ -62,19 +64,19 @@ note("\$Data::Dumper::Deparse and Deparse()");
6264
$dumps{'objone'} = _dumptostr($obj);
6365

6466
is($dumps{'dddtrue'}, $dumps{'objone'},
65-
"\$Data::Dumper::Deparse = 1 and Deparse(1) are equivalent");
67+
"\$Data::Dumper::Deparse = 1 and Deparse(1) are equivalent (useperl=$useperl)");
6668

6769
isnt($dumps{'dddzero'}, $dumps{'dddtrue'},
68-
"\$Data::Dumper::Deparse = 0 differs from \$Data::Dumper::Deparse = 1");
70+
"\$Data::Dumper::Deparse = 0 differs from \$Data::Dumper::Deparse = 1 (useperl=$useperl)");
6971

7072
like($dumps{'dddzero'},
7173
qr/quux.*?sub.*?DUMMY/s,
72-
"\$Data::Dumper::Deparse = 0 reports DUMMY instead of deparsing coderef");
74+
"\$Data::Dumper::Deparse = 0 reports DUMMY instead of deparsing coderef (useperl=$useperl)");
7375
unlike($dumps{'dddtrue'},
7476
qr/quux.*?sub.*?DUMMY/s,
75-
"\$Data::Dumper::Deparse = 1 does not report DUMMY");
77+
"\$Data::Dumper::Deparse = 1 does not report DUMMY (useperl=$useperl)");
7678
like($dumps{'dddtrue'},
7779
qr/quux.*?sub.*?use\sstrict.*?fleem/s,
78-
"\$Data::Dumper::Deparse = 1 deparses coderef");
80+
"\$Data::Dumper::Deparse = 1 deparses coderef (useperl=$useperl)");
7981
}
8082

0 commit comments

Comments
 (0)