Perl_newHVhv() should copy immortal values as-is, such as PL_sv_undef
authorNicholas Clark <nick@ccl4.org>
Thu, 20 Aug 2009 14:56:18 +0000 (15:56 +0100)
committerNicholas Clark <nick@ccl4.org>
Thu, 20 Aug 2009 14:56:18 +0000 (15:56 +0100)
Currently it calls newSVsv() always, which copies the value, but the immortal
SVs are used as much for their addresses as their values. You can't get the
immortals into HVs from Perl-space, except for PL_sv_placeholder, and any hash
with those will take the else block, where the call to Perl_hv_iternext_flags()
won't be returning placeholders anyway. Hence If XS code has gone to the
trouble to get the "impossible" in there, they had a reason for it.

I am assuming that Perl_hv_copy_hints_hv() should stay as-is, as it is
documented that only strings and integers are supported values for %^H.

hv.c

diff --git a/hv.c b/hv.c
index a5221a8..ee3a67e 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -1379,8 +1379,9 @@ Perl_newHVhv(pTHX_ HV *ohv)
                const STRLEN len = HeKLEN(oent);
                const int flags  = HeKFLAGS(oent);
                HE * const ent   = new_HE();
+               SV *const val    = HeVAL(oent);
 
-               HeVAL(ent)     = newSVsv(HeVAL(oent));
+               HeVAL(ent) = SvIMMORTAL(val) ? val : newSVsv(val);
                HeKEY_hek(ent)
                     = shared ? share_hek_flags(key, len, hash, flags)
                              :  save_hek_flags(key, len, hash, flags);
@@ -1411,9 +1412,10 @@ Perl_newHVhv(pTHX_ HV *ohv)
 
        hv_iterinit(ohv);
        while ((entry = hv_iternext_flags(ohv, 0))) {
+           SV *const val = HeVAL(entry);
            (void)hv_store_flags(hv, HeKEY(entry), HeKLEN(entry),
-                                newSVsv(HeVAL(entry)), HeHASH(entry),
-                                HeKFLAGS(entry));
+                                SvIMMORTAL(val) ? val : newSVsv(val),
+                                HeHASH(entry), HeKFLAGS(entry));
        }
        HvRITER_set(ohv, riter);
        HvEITER_set(ohv, eiter);