This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
make postav and valstr mortal so they're freed soonish
[perl5.git] / dist / Data-Dumper / Dumper.xs
index 9294645..d4b34ad 100644 (file)
 #  define DD_USE_OLD_ID_FORMAT
 #endif
 
+#ifndef strlcpy
+#  ifdef my_strlcpy
+#    define strlcpy(d,s,l) my_strlcpy(d,s,l)
+#  else
+#    define strlcpy(d,s,l) strcpy(d,s)
+#  endif
+#endif
+
 /* These definitions are ASCII only.  But the pure-perl .pm avoids
  * calling this .xs file for releases where they aren't defined */
 
                           || (((UV) (c)) >= '0' && ((UV) (c)) <= '9'))
 #endif
 
+/* SvPVCLEAR only from perl 5.25.6 */
+#ifndef SvPVCLEAR
+#  define SvPVCLEAR(sv) sv_setpvs((sv), "")
+#endif
+
+#ifndef memBEGINs
+#  define memBEGINs(s1, l, s2)                                              \
+            (   (l) >= sizeof(s2) - 1                                       \
+             && memEQ(s1, "" s2 "", sizeof(s2)-1))
+#endif
+
 /* This struct contains almost all the user's desired configuration, and it
- * is treated as constant by the recursive function. This arrangement has
- * the advantage of needing less memory than passing all of them on the
- * stack all the time (as was the case in an earlier implementation). But
- * this means that, for example, "sortkeys" is a separate parameter. */
+ * is treated as mostly constant (except for maxrecursed) by the recursive
+ * function.  This arrangement has the advantage of needing less memory
+ * than passing all of them on the stack all the time (as was the case in
+ * an earlier implementation). */
 typedef struct {
     SV *pad;
     SV *xpad;
     SV *sep;
     SV *pair;
+    SV *sortkeys;
     SV *freezer;
     SV *toaster;
     SV *bless;
     IV maxrecurse;
+    bool maxrecursed; /* at some point we exceeded the maximum recursion level */
     I32 indent;
     I32 purity;
     I32 deepcopy;
@@ -62,18 +83,23 @@ typedef struct {
     I32 maxdepth;
     I32 useqq;
     int use_sparse_seen_hash;
+    int trailingcomma;
+    int deparse;
 } Style;
 
-static I32 num_q (const char *s, STRLEN slen);
-static I32 esc_q (char *dest, const char *src, STRLEN slen);
-static I32 esc_q_utf8 (pTHX_ SV *sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq);
+static STRLEN num_q (const char *s, STRLEN slen);
+static STRLEN esc_q (char *dest, const char *src, STRLEN slen);
+static STRLEN esc_q_utf8 (pTHX_ SV *sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq);
 static bool globname_needs_quote(const char *s, STRLEN len);
+#ifndef GvNAMEUTF8
+static bool globname_supra_ascii(const char *s, STRLEN len);
+#endif
 static bool key_needs_quote(const char *s, STRLEN len);
 static bool safe_decimal_number(const char *p, STRLEN len);
 static SV *sv_x (pTHX_ SV *sv, const char *str, STRLEN len, I32 n);
 static I32 DD_dump (pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval,
-                    HV *seenhv, AV *postav, I32 *levelp, SV *apad, SV *sortkeys,
-                    const Style *style);
+                    HV *seenhv, AV *postav, const I32 level, SV *apad,
+                    Style *style);
 
 #ifndef HvNAME_get
 #define HvNAME_get HvNAME
@@ -133,9 +159,10 @@ Perl_utf8_to_uvchr_buf(pTHX_ U8 *s, U8 *send, STRLEN *retlen)
 
 /* does a glob name need to be protected? */
 static bool
-globname_needs_quote(const char *s, STRLEN len)
+globname_needs_quote(const char *ss, STRLEN len)
 {
-    const char *send = s+len;
+    const U8 *s = (const U8 *) ss;
+    const U8 *send = s+len;
 TOP:
     if (s[0] == ':') {
        if (++s<send) {
@@ -160,6 +187,22 @@ TOP:
     return FALSE;
 }
 
+#ifndef GvNAMEUTF8
+/* does a glob name contain supra-ASCII characters? */
+static bool
+globname_supra_ascii(const char *ss, STRLEN len)
+{
+    const U8 *s = (const U8 *) ss;
+    const U8 *send = s+len;
+    while (s < send) {
+        if (!isASCII(*s))
+            return TRUE;
+        s++;
+    }
+    return FALSE;
+}
+#endif
+
 /* does a hash key need to be quoted (to the left of => ).
    Previously this used (globname_)needs_quote() which accepted strings
    like '::foo', but these aren't safe as unquoted keys under strict.
@@ -216,10 +259,10 @@ safe_decimal_number(const char *p, STRLEN len) {
 }
 
 /* count the number of "'"s and "\"s in string */
-static I32
+static STRLEN
 num_q(const char *s, STRLEN slen)
 {
-    I32 ret = 0;
+    STRLEN ret = 0;
 
     while (slen > 0) {
        if (*s == '\'' || *s == '\\')
@@ -234,10 +277,10 @@ num_q(const char *s, STRLEN slen)
 /* returns number of chars added to escape "'"s and "\"s in s */
 /* slen number of characters in s will be escaped */
 /* destination must be long enough for additional chars */
-static I32
+static STRLEN
 esc_q(char *d, const char *s, STRLEN slen)
 {
-    I32 ret = 0;
+    STRLEN ret = 0;
 
     while (slen > 0) {
        switch (*s) {
@@ -256,7 +299,7 @@ esc_q(char *d, const char *s, STRLEN slen)
 }
 
 /* this function is also misused for implementing $Useqq */
-static I32
+static STRLEN
 esc_q_utf8(pTHX_ SV* sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq)
 {
     char *r, *rstart;
@@ -365,15 +408,16 @@ esc_q_utf8(pTHX_ SV* sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq)
         *r++ = '"';
 
         for (s = src; s < send; s += increment) {
+            U8 c0 = *(U8 *)s;
             UV k;
 
             if (do_utf8
-                && ! isASCII(*s)
+                && ! isASCII(c0)
                     /* Exclude non-ASCII low ordinal controls.  This should be
                      * optimized out by the compiler on ASCII platforms; if not
                      * could wrap it in a #ifdef EBCDIC, but better to avoid
                      * #if's if possible */
-                && *(U8*)s > ' '
+                && c0 > ' '
             ) {
 
                 /* When in UTF-8, we output all non-ascii chars as \x{}
@@ -386,11 +430,11 @@ esc_q_utf8(pTHX_ SV* sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq)
                 increment = (k == 0 && *s != '\0') ? 1 : UTF8SKIP(s);
 
 #if PERL_VERSION < 10
-                sprintf(r, "\\x{%"UVxf"}", k);
+                sprintf(r, "\\x{%" UVxf "}", k);
                 r += strlen(r);
                 /* my_sprintf is not supported by ppport.h */
 #else
-                r = r + my_sprintf(r, "\\x{%"UVxf"}", k);
+                r = r + my_sprintf(r, "\\x{%" UVxf "}", k);
 #endif
                 continue;
             }
@@ -504,6 +548,68 @@ sv_x(pTHX_ SV *sv, const char *str, STRLEN len, I32 n)
     return sv;
 }
 
+static SV *
+deparsed_output(pTHX_ SV *val)
+{
+    SV *text;
+    int n;
+    dSP;
+
+    /* This is passed to load_module(), which decrements its ref count and
+     * modifies it (so we also can't reuse it below) */
+    SV *pkg = newSVpvs("B::Deparse");
+
+    /* Commit ebdc88085efa6fca8a1b0afaa388f0491bdccd5a (first released as part
+     * of 5.19.7) changed core S_process_special_blocks() to use a new stack
+     * for anything using a BEGIN block, on the grounds that doing so "avoids
+     * the stack moving underneath anything that directly or indirectly calls
+     * Perl_load_module()". If we're in an older Perl, we can't rely on that
+     * stack, and must create a fresh sacrificial stack of our own. */
+#if PERL_VERSION < 20
+    PUSHSTACKi(PERLSI_REQUIRE);
+#endif
+
+    load_module(PERL_LOADMOD_NOIMPORT, pkg, 0);
+
+#if PERL_VERSION < 20
+    POPSTACK;
+    SPAGAIN;
+#endif
+
+    SAVETMPS;
+
+    PUSHMARK(SP);
+    mXPUSHs(newSVpvs("B::Deparse"));
+    PUTBACK;
+
+    n = call_method("new", G_SCALAR);
+    SPAGAIN;
+
+    if (n != 1) {
+        croak("B::Deparse->new returned %d items, but expected exactly 1", n);
+    }
+
+    PUSHMARK(SP - n);
+    XPUSHs(val);
+    PUTBACK;
+
+    n = call_method("coderef2text", G_SCALAR);
+    SPAGAIN;
+
+    if (n != 1) {
+        croak("$b_deparse->coderef2text returned %d items, but expected exactly 1", n);
+    }
+
+    text = POPs;
+    SvREFCNT_inc(text);         /* the caller will mortalise this */
+
+    FREETMPS;
+
+    PUTBACK;
+
+    return text;
+}
+
 /*
  * This ought to be split into smaller functions. (it is one long function since
  * it exactly parallels the perl version, which was one long thing for
@@ -511,7 +617,7 @@ sv_x(pTHX_ SV *sv, const char *str, STRLEN len, I32 n)
  */
 static I32
 DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
-       AV *postav, I32 *levelp, SV *apad, SV *sortkeys, const Style *style)
+       AV *postav, const I32 level, SV *apad, Style *style)
 {
     char tmpbuf[128];
     Size_t i;
@@ -538,6 +644,9 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
     if (!val)
        return 0;
 
+    if (style->maxrecursed)
+        return 0;
+
     /* If the output buffer has less than some arbitrary amount of space
        remaining, then enlarge it. For the test case (25M of output),
        *1.1 was slower, *2.0 was the same, so the first guess of 1.5 is
@@ -564,14 +673,14 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
             i = perl_call_method(SvPVX_const(style->freezer), G_EVAL|G_VOID|G_DISCARD);
            SPAGAIN;
            if (SvTRUE(ERRSV))
-               warn("WARNING(Freezer method call failed): %"SVf"", ERRSV);
+               warn("WARNING(Freezer method call failed): %" SVf, ERRSV);
            PUTBACK; FREETMPS; LEAVE;
        }
        
        ival = SvRV(val);
        realtype = SvTYPE(ival);
 #ifdef DD_USE_OLD_ID_FORMAT
-        idlen = my_snprintf(id, sizeof(id), "0x%"UVxf, PTR2UV(ival));
+        idlen = my_snprintf(id, sizeof(id), "0x%" UVxf, PTR2UV(ival));
 #else
        id_buffer = PTR2UV(ival);
        idlen = sizeof(id_buffer);
@@ -592,7 +701,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                if ((svp = av_fetch(seenentry, 0, FALSE))
                    && (othername = *svp))
                {
-                   if (style->purity && *levelp > 0) {
+                   if (style->purity && level > 0) {
                        SV *postentry;
                        
                        if (realtype == SVt_PVHV)
@@ -629,7 +738,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
 #ifdef DD_USE_OLD_ID_FORMAT
                    warn("ref name not found for %s", id);
 #else
-                   warn("ref name not found for 0x%"UVxf, PTR2UV(ival));
+                   warn("ref name not found for 0x%" UVxf, PTR2UV(ival));
 #endif
                    return 0;
                }
@@ -679,7 +788,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
         * representation of the thing we are currently examining
         * at this depth (i.e., 'Foo=ARRAY(0xdeadbeef)').
         */
-        if (!style->purity && style->maxdepth > 0 && *levelp >= style->maxdepth) {
+        if (!style->purity && style->maxdepth > 0 && level >= style->maxdepth) {
            STRLEN vallen;
            const char * const valstr = SvPV(val,vallen);
            sv_catpvs(retval, "'");
@@ -688,8 +797,8 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            return 1;
        }
 
-        if (style->maxrecurse > 0 && *levelp >= style->maxrecurse) {
-            croak("Recursion limit of %" IVdf " exceeded", style->maxrecurse);
+        if (style->maxrecurse > 0 && level >= style->maxrecurse) {
+            style->maxrecursed = TRUE;
        }
 
        if (realpack && !no_bless) {                            /* we have a blessed ref */
@@ -704,8 +813,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            }
        }
 
-       (*levelp)++;
-        ipad = sv_x(aTHX_ Nullsv, SvPVX_const(style->xpad), SvCUR(style->xpad), *levelp);
+        ipad = sv_x(aTHX_ Nullsv, SvPVX_const(style->xpad), SvCUR(style->xpad), level+1);
 
         if (is_regex) 
         {
@@ -776,13 +884,13 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            if (realpack) {                                  /* blessed */
                sv_catpvs(retval, "do{\\(my $o = ");
                DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv,
-                       postav, levelp, apad, sortkeys, style);
+                       postav, level+1, apad, style);
                sv_catpvs(retval, ")}");
            }                                                /* plain */
            else {
                sv_catpvs(retval, "\\");
                DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv,
-                       postav, levelp, apad, sortkeys, style);
+                       postav, level+1, apad, style);
            }
            SvREFCNT_dec(namesv);
        }
@@ -792,7 +900,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            sv_catpvs(namesv, "}");
            sv_catpvs(retval, "\\");
            DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv,
-                   postav, levelp, apad, sortkeys, style);
+                   postav, level+1, apad, style);
            SvREFCNT_dec(namesv);
        }
        else if (realtype == SVt_PVAV) {
@@ -803,7 +911,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            SV * const ixsv = newSViv(0);
            /* allowing for a 24 char wide array index */
            New(0, iname, namelen+28, char);
-           (void)strcpy(iname, name);
+           (void) strlcpy(iname, name, namelen+28);
            inamelen = namelen;
            if (name[0] == '@') {
                sv_catpvs(retval, "(");
@@ -848,10 +956,10 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                ilen = inamelen;
                sv_setiv(ixsv, ix);
 #if PERL_VERSION < 10
-                (void) sprintf(iname+ilen, "%"IVdf, (IV)ix);
+                (void) sprintf(iname+ilen, "%" IVdf, (IV)ix);
                ilen = strlen(iname);
 #else
-                ilen = ilen + my_sprintf(iname+ilen, "%"IVdf, (IV)ix);
+                ilen = ilen + my_sprintf(iname+ilen, "%" IVdf, (IV)ix);
 #endif
                iname[ilen++] = ']'; iname[ilen] = '\0';
                 if (style->indent >= 3) {
@@ -863,12 +971,12 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                sv_catsv(retval, totpad);
                sv_catsv(retval, ipad);
                DD_dump(aTHX_ elem, iname, ilen, retval, seenhv, postav,
-                       levelp, apad, sortkeys, style);
-               if (ix < ixmax)
+                       level+1, apad, style);
+               if (ix < ixmax || (style->trailingcomma && style->indent >= 1))
                    sv_catpvs(retval, ",");
            }
            if (ixmax >= 0) {
-                SV * const opad = sv_x(aTHX_ Nullsv, SvPVX_const(style->xpad), SvCUR(style->xpad), (*levelp)-1);
+                SV * const opad = sv_x(aTHX_ Nullsv, SvPVX_const(style->xpad), SvCUR(style->xpad), level);
                sv_catsv(retval, totpad);
                sv_catsv(retval, opad);
                SvREFCNT_dec(opad);
@@ -886,7 +994,6 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            SV *sname;
            HE *entry = NULL;
            char *key;
-           I32 klen;
            SV *hval;
            AV *keys = NULL;
        
@@ -919,11 +1026,9 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            sv_catsv(totpad, apad);
        
            /* If requested, get a sorted/filtered array of hash keys */
-           if (sortkeys) {
-               if (sortkeys == &PL_sv_yes) {
-#if PERL_VERSION < 8
-                    sortkeys = sv_2mortal(newSVpvs("Data::Dumper::_sortkeys"));
-#else
+           if (style->sortkeys) {
+#if PERL_VERSION >= 8
+               if (style->sortkeys == &PL_sv_yes) {
                    keys = newAV();
                    (void)hv_iterinit((HV*)ival);
                    while ((entry = hv_iternext((HV*)ival))) {
@@ -944,17 +1049,18 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                     }
                     else
 # endif
-#endif
                     {
                         sortsv(AvARRAY(keys),
                           av_len(keys)+1,
                            Perl_sv_cmp);
                     }
                }
-               if (sortkeys != &PL_sv_yes) {
+                else
+#endif
+               {
                    dSP; ENTER; SAVETMPS; PUSHMARK(sp);
                    XPUSHs(sv_2mortal(newRV_inc(ival))); PUTBACK;
-                   i = perl_call_sv(sortkeys, G_SCALAR | G_EVAL);
+                   i = perl_call_sv(style->sortkeys, G_SCALAR | G_EVAL);
                    SPAGAIN;
                    if (i) {
                        sv = POPs;
@@ -975,13 +1081,14 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
             for (i = 0; 1; i++) {
                char *nkey;
                 char *nkey_buffer = NULL;
-               I32 nticks = 0;
+                STRLEN nticks = 0;
                SV* keysv;
+                STRLEN klen;
                STRLEN keylen;
-                I32 nlen;
+                STRLEN nlen;
                bool do_utf8 = FALSE;
 
-               if (sortkeys) {
+               if (style->sortkeys) {
                    if (!(keys && (SSize_t)i <= av_len(keys))) break;
                } else {
                    if (!(entry = hv_iternext((HV *)ival))) break;
@@ -990,7 +1097,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                if (i)
                    sv_catpvs(retval, ",");
 
-               if (sortkeys) {
+               if (style->sortkeys) {
                    char *key;
                    svp = av_fetch(keys, i, FALSE);
                    keysv = svp ? *svp : sv_newmortal();
@@ -1030,7 +1137,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                 if (style->quotekeys || key_needs_quote(key,keylen)) {
                     if (do_utf8 || style->useqq) {
                         STRLEN ocur = SvCUR(retval);
-                        nlen = esc_q_utf8(aTHX_ retval, key, klen, do_utf8, style->useqq);
+                        klen = nlen = esc_q_utf8(aTHX_ retval, key, klen, do_utf8, style->useqq);
                         nkey = SvPVX(retval) + ocur;
                     }
                     else {
@@ -1060,7 +1167,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                 sv_catsv(retval, style->pair);
                 if (style->indent >= 2) {
                    char *extra;
-                   I32 elen = 0;
+                    STRLEN elen = 0;
                    newapad = newSVsv(apad);
                    New(0, extra, klen+4+1, char);
                    while (elen < (klen+4))
@@ -1073,7 +1180,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                    newapad = apad;
 
                DD_dump(aTHX_ hval, SvPVX_const(sname), SvCUR(sname), retval, seenhv,
-                       postav, levelp, newapad, sortkeys, style);
+                       postav, level+1, newapad, style);
                SvREFCNT_dec(sname);
                Safefree(nkey_buffer);
                 if (style->indent >= 2)
@@ -1081,7 +1188,9 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            }
            if (i) {
                 SV *opad = sv_x(aTHX_ Nullsv, SvPVX_const(style->xpad),
-                                SvCUR(style->xpad), *levelp-1);
+                                SvCUR(style->xpad), level);
+                if (style->trailingcomma && style->indent >= 1)
+                    sv_catpvs(retval, ",");
                sv_catsv(retval, totpad);
                sv_catsv(retval, opad);
                SvREFCNT_dec(opad);
@@ -1094,17 +1203,48 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            SvREFCNT_dec(totpad);
        }
        else if (realtype == SVt_PVCV) {
-           sv_catpvs(retval, "sub { \"DUMMY\" }");
-            if (style->purity)
-               warn("Encountered CODE ref, using dummy placeholder");
+            if (style->deparse) {
+                SV *deparsed = sv_2mortal(deparsed_output(aTHX_ val));
+                SV *fullpad = sv_2mortal(newSVsv(style->sep));
+                const char *p;
+                STRLEN plen;
+                I32 i;
+
+                sv_catsv(fullpad, style->pad);
+                sv_catsv(fullpad, apad);
+                for (i = 0; i < level; i++) {
+                    sv_catsv(fullpad, style->xpad);
+                }
+
+                sv_catpvs(retval, "sub ");
+                p = SvPV(deparsed, plen);
+                while (plen > 0) {
+                    const char *nl = (const char *) memchr(p, '\n', plen);
+                    if (!nl) {
+                        sv_catpvn(retval, p, plen);
+                        break;
+                    }
+                    else {
+                        size_t n = nl - p;
+                        sv_catpvn(retval, p, n);
+                        sv_catsv(retval, fullpad);
+                        p += n + 1;
+                        plen -= n + 1;
+                    }
+                }
+            }
+            else {
+                sv_catpvs(retval, "sub { \"DUMMY\" }");
+                if (style->purity)
+                    warn("Encountered CODE ref, using dummy placeholder");
+            }
        }
        else {
            warn("cannot handle ref type %d", (int)realtype);
        }
 
        if (realpack && !no_bless) {  /* free blessed allocs */
-           I32 plen;
-           I32 pticks;
+            STRLEN plen, pticks;
 
             if (style->indent >= 2) {
                SvREFCNT_dec(apad);
@@ -1137,7 +1277,6 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            }
        }
        SvREFCNT_dec(ipad);
-       (*levelp)--;
     }
     else {
        STRLEN i;
@@ -1145,7 +1284,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
        
        if (namelen) {
 #ifdef DD_USE_OLD_ID_FORMAT
-           idlen = my_snprintf(id, sizeof(id), "0x%"UVxf, PTR2UV(val));
+           idlen = my_snprintf(id, sizeof(id), "0x%" UVxf, PTR2UV(val));
 #else
            id_buffer = PTR2UV(val);
            idlen = sizeof(id_buffer);
@@ -1185,9 +1324,9 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
         if (DD_is_integer(val)) {
             STRLEN len;
            if (SvIsUV(val))
-             len = my_snprintf(tmpbuf, sizeof(tmpbuf), "%"UVuf, SvUV(val));
+             len = my_snprintf(tmpbuf, sizeof(tmpbuf), "%" UVuf, SvUV(val));
            else
-             len = my_snprintf(tmpbuf, sizeof(tmpbuf), "%"IVdf, SvIV(val));
+             len = my_snprintf(tmpbuf, sizeof(tmpbuf), "%" IVdf, SvIV(val));
             if (SvPOK(val)) {
               /* Need to check to see if this is a string such as " 0".
                  I'm assuming from sprintf isn't going to clash with utf8. */
@@ -1206,7 +1345,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
        else if (realtype == SVt_PVGV) {/* GLOBs can end up with scribbly names */
            c = SvPV(val, i);
            if(i) ++c, --i;                     /* just get the name */
-           if (i >= 6 && strncmp(c, "main::", 6) == 0) {
+           if (memBEGINs(c, i, "main::")) {
                c += 4;
 #if PERL_VERSION < 7
                if (i == 6 || (i == 7 && c[6] == '\0'))
@@ -1216,37 +1355,30 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                    i = 0; else i -= 4;
            }
             if (globname_needs_quote(c,i)) {
-#ifdef GvNAMEUTF8
-             if (GvNAMEUTF8(val)) {
-               sv_grow(retval, SvCUR(retval)+2);
+               sv_grow(retval, SvCUR(retval)+3);
                r = SvPVX(retval)+SvCUR(retval);
-               r[0] = '*'; r[1] = '{';
+               r[0] = '*'; r[1] = '{'; r[2] = 0;
                SvCUR_set(retval, SvCUR(retval)+2);
-                esc_q_utf8(aTHX_ retval, c, i, 1, style->useqq);
+                i = 3 + esc_q_utf8(aTHX_ retval, c, i,
+#ifdef GvNAMEUTF8
+                       !!GvNAMEUTF8(val), style->useqq
+#else
+                       0, style->useqq || globname_supra_ascii(c, i)
+#endif
+                       );
                sv_grow(retval, SvCUR(retval)+2);
                r = SvPVX(retval)+SvCUR(retval);
                r[0] = '}'; r[1] = '\0';
-               i = 1;
-             }
-             else
-#endif
-             {
-               sv_grow(retval, SvCUR(retval)+6+2*i);
-               r = SvPVX(retval)+SvCUR(retval);
-               r[0] = '*'; r[1] = '{'; r[2] = '\'';
-               i += esc_q(r+3, c, i);
-               i += 3;
-               r[i++] = '\''; r[i++] = '}';
-               r[i] = '\0';
-             }
+               SvCUR_set(retval, SvCUR(retval)+1);
+               r = r+1 - i;
            }
            else {
                sv_grow(retval, SvCUR(retval)+i+2);
                r = SvPVX(retval)+SvCUR(retval);
-               r[0] = '*'; strcpy(r+1, c);
+               r[0] = '*'; strlcpy(r+1, c, SvLEN(retval));
                i++;
+               SvCUR_set(retval, SvCUR(retval)+i);
            }
-           SvCUR_set(retval, SvCUR(retval)+i);
 
             if (style->purity) {
                static const char* const entries[] = { "{SCALAR}", "{ARRAY}", "{HASH}" };
@@ -1265,7 +1397,6 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                        continue;
 
                    {
-                       I32 nlevel = 0;
                        SV *postentry = newSVpvn(r,i);
                        
                        sv_setsv(nname, postentry);
@@ -1279,7 +1410,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                            (void)sv_x(aTHX_ newapad, " ", 1, SvCUR(postentry));
                        
                        DD_dump(aTHX_ e, SvPVX_const(nname), SvCUR(nname), postentry,
-                               seenhv, postav, &nlevel, newapad, sortkeys, style);
+                               seenhv, postav, 0, newapad, style);
                        SvREFCNT_dec(e);
                    }
                }
@@ -1362,12 +1493,10 @@ Data_Dumper_Dumpxs(href, ...)
            SV *retval, *valstr;
            HV *seenhv = NULL;
            AV *postav, *todumpav, *namesav;
-           I32 level = 0;
            I32 terse = 0;
            SSize_t i, imax, postlen;
            SV **svp;
             SV *apad = &PL_sv_undef;
-            SV *sortkeys = &PL_sv_undef;
             Style style;
 
             SV *name, *val = &PL_sv_undef, *varname = &PL_sv_undef;
@@ -1404,80 +1533,93 @@ Data_Dumper_Dumpxs(href, ...)
             style.indent = 2;
             style.quotekeys = 1;
             style.maxrecurse = 1000;
+            style.maxrecursed = FALSE;
             style.purity = style.deepcopy = style.useqq = style.maxdepth
-                = style.use_sparse_seen_hash = 0;
-            style.pad = style.xpad = style.sep = style.pair
+                = style.use_sparse_seen_hash = style.trailingcomma = 0;
+            style.pad = style.xpad = style.sep = style.pair = style.sortkeys
                 = style.freezer = style.toaster = style.bless = &PL_sv_undef;
            seenhv = NULL;
            name = sv_newmortal();
        
-           retval = newSVpvs("");
+           retval = newSVpvs_flags("", SVs_TEMP);
            if (SvROK(href)
                && (hv = (HV*)SvRV((SV*)href))
                && SvTYPE(hv) == SVt_PVHV)              {
 
-               if ((svp = hv_fetch(hv, "seen", 4, FALSE)) && SvROK(*svp))
+               if ((svp = hv_fetchs(hv, "seen", FALSE)) && SvROK(*svp))
                    seenhv = (HV*)SvRV(*svp);
                 else
                     style.use_sparse_seen_hash = 1;
-               if ((svp = hv_fetch(hv, "noseen", 6, FALSE)))
+               if ((svp = hv_fetchs(hv, "noseen", FALSE)))
                     style.use_sparse_seen_hash = (SvOK(*svp) && SvIV(*svp) != 0);
-               if ((svp = hv_fetch(hv, "todump", 6, FALSE)) && SvROK(*svp))
+               if ((svp = hv_fetchs(hv, "todump", FALSE)) && SvROK(*svp))
                    todumpav = (AV*)SvRV(*svp);
-               if ((svp = hv_fetch(hv, "names", 5, FALSE)) && SvROK(*svp))
+               if ((svp = hv_fetchs(hv, "names", FALSE)) && SvROK(*svp))
                    namesav = (AV*)SvRV(*svp);
-               if ((svp = hv_fetch(hv, "indent", 6, FALSE)))
+               if ((svp = hv_fetchs(hv, "indent", FALSE)))
                     style.indent = SvIV(*svp);
-               if ((svp = hv_fetch(hv, "purity", 6, FALSE)))
+               if ((svp = hv_fetchs(hv, "purity", FALSE)))
                     style.purity = SvIV(*svp);
-               if ((svp = hv_fetch(hv, "terse", 5, FALSE)))
+               if ((svp = hv_fetchs(hv, "terse", FALSE)))
                    terse = SvTRUE(*svp);
-               if ((svp = hv_fetch(hv, "useqq", 5, FALSE)))
+               if ((svp = hv_fetchs(hv, "useqq", FALSE)))
                     style.useqq = SvTRUE(*svp);
-               if ((svp = hv_fetch(hv, "pad", 3, FALSE)))
+               if ((svp = hv_fetchs(hv, "pad", FALSE)))
                     style.pad = *svp;
-               if ((svp = hv_fetch(hv, "xpad", 4, FALSE)))
+               if ((svp = hv_fetchs(hv, "xpad", FALSE)))
                     style.xpad = *svp;
-               if ((svp = hv_fetch(hv, "apad", 4, FALSE)))
+               if ((svp = hv_fetchs(hv, "apad", FALSE)))
                    apad = *svp;
-               if ((svp = hv_fetch(hv, "sep", 3, FALSE)))
+               if ((svp = hv_fetchs(hv, "sep", FALSE)))
                     style.sep = *svp;
-               if ((svp = hv_fetch(hv, "pair", 4, FALSE)))
+               if ((svp = hv_fetchs(hv, "pair", FALSE)))
                     style.pair = *svp;
-               if ((svp = hv_fetch(hv, "varname", 7, FALSE)))
+               if ((svp = hv_fetchs(hv, "varname", FALSE)))
                    varname = *svp;
-               if ((svp = hv_fetch(hv, "freezer", 7, FALSE)))
+               if ((svp = hv_fetchs(hv, "freezer", FALSE)))
                     style.freezer = *svp;
-               if ((svp = hv_fetch(hv, "toaster", 7, FALSE)))
+               if ((svp = hv_fetchs(hv, "toaster", FALSE)))
                     style.toaster = *svp;
-               if ((svp = hv_fetch(hv, "deepcopy", 8, FALSE)))
+               if ((svp = hv_fetchs(hv, "deepcopy", FALSE)))
                     style.deepcopy = SvTRUE(*svp);
-               if ((svp = hv_fetch(hv, "quotekeys", 9, FALSE)))
+               if ((svp = hv_fetchs(hv, "quotekeys", FALSE)))
                     style.quotekeys = SvTRUE(*svp);
-               if ((svp = hv_fetch(hv, "bless", 5, FALSE)))
+                if ((svp = hv_fetchs(hv, "trailingcomma", FALSE)))
+                    style.trailingcomma = SvTRUE(*svp);
+                if ((svp = hv_fetchs(hv, "deparse", FALSE)))
+                    style.deparse = SvTRUE(*svp);
+               if ((svp = hv_fetchs(hv, "bless", FALSE)))
                     style.bless = *svp;
-               if ((svp = hv_fetch(hv, "maxdepth", 8, FALSE)))
+               if ((svp = hv_fetchs(hv, "maxdepth", FALSE)))
                     style.maxdepth = SvIV(*svp);
-               if ((svp = hv_fetch(hv, "maxrecurse", 10, FALSE)))
+               if ((svp = hv_fetchs(hv, "maxrecurse", FALSE)))
                     style.maxrecurse = SvIV(*svp);
-               if ((svp = hv_fetch(hv, "sortkeys", 8, FALSE))) {
-                   sortkeys = *svp;
-                   if (! SvTRUE(sortkeys))
-                       sortkeys = NULL;
-                   else if (! (SvROK(sortkeys) &&
-                               SvTYPE(SvRV(sortkeys)) == SVt_PVCV) )
-                   {
-                       /* flag to use qsortsv() for sorting hash keys */       
-                       sortkeys = &PL_sv_yes; 
-                   }
+               if ((svp = hv_fetchs(hv, "sortkeys", FALSE))) {
+                    SV *sv = *svp;
+                    if (! SvTRUE(sv))
+                        style.sortkeys = NULL;
+                    else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
+                        style.sortkeys = sv;
+                    else if (PERL_VERSION < 8)
+                        /* 5.6 doesn't make sortsv() available to XS code,
+                         * so we must use this helper instead. Note that we
+                         * always allocate this mortal SV, but it will be
+                         * used only if at least one hash is encountered
+                         * while dumping recursively; an older version
+                         * allocated it lazily as needed. */
+                        style.sortkeys = sv_2mortal(newSVpvs("Data::Dumper::_sortkeys"));
+                    else
+                        /* flag to use sortsv() for sorting hash keys */
+                        style.sortkeys = &PL_sv_yes;
                }
                postav = newAV();
+                sv_2mortal((SV*)postav);
 
                if (todumpav)
                    imax = av_len(todumpav);
                else
                    imax = -1;
-               valstr = newSVpvs("");
+               valstr = newSVpvs_flags("", SVs_TEMP);
                for (i = 0; i <= imax; ++i) {
                    SV *newapad;
                
@@ -1520,9 +1662,10 @@ Data_Dumper_Dumpxs(href, ...)
                    }
                    else {
                        STRLEN nchars;
-                       sv_setpvn(name, "$", 1);
+                       sv_setpvs(name, "$");
                        sv_catsv(name, varname);
-                       nchars = my_snprintf(tmpbuf, sizeof(tmpbuf), "%"IVdf, (IV)(i+1));
+                       nchars = my_snprintf(tmpbuf, sizeof(tmpbuf), "%" IVdf,
+                                                                     (IV)(i+1));
                        sv_catpvn(name, tmpbuf, nchars);
                    }
                
@@ -1537,9 +1680,9 @@ Data_Dumper_Dumpxs(href, ...)
                
                    PUTBACK;
                    DD_dump(aTHX_ val, SvPVX_const(name), SvCUR(name), valstr, seenhv,
-                            postav, &level, newapad, sortkeys, &style);
+                            postav, 0, newapad, &style);
                    SPAGAIN;
-               
+
                     if (style.indent >= 2 && !terse)
                        SvREFCNT_dec(newapad);
 
@@ -1570,20 +1713,25 @@ Data_Dumper_Dumpxs(href, ...)
                        sv_catpvs(retval, ";");
                         sv_catsv(retval, style.sep);
                    }
-                   sv_setpvn(valstr, "", 0);
+                   SvPVCLEAR(valstr);
                    if (gimme == G_ARRAY) {
-                       XPUSHs(sv_2mortal(retval));
+                       XPUSHs(retval);
                        if (i < imax)   /* not the last time thro ? */
-                           retval = newSVpvs("");
+                           retval = newSVpvs_flags("", SVs_TEMP);
                    }
                }
-               SvREFCNT_dec(postav);
-               SvREFCNT_dec(valstr);
+
+                /* we defer croaking until here so that temporary SVs and
+                 * buffers won't be leaked */
+                if (style.maxrecursed)
+                    croak("Recursion limit of %" IVdf " exceeded",
+                            style.maxrecurse);
+               
            }
            else
                croak("Call to new() method failed to return HASH ref");
            if (gimme != G_ARRAY)
-               XPUSHs(sv_2mortal(retval));
+               XPUSHs(retval);
        }
 
 SV *