[perl #3096] undefing hash with object values
Dave Mitchell [Thu, 1 May 2003 21:06:57 +0000 (22:06 +0100)]
Message-ID: <20030501200657.GA25456@fdgroup.com>

p4raw-id: //depot/perl@19424

hv.c
t/op/undef.t

diff --git a/hv.c b/hv.c
index e018a75..f5508bf 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -1728,8 +1728,6 @@ Perl_hv_clear(pTHX_ HV *hv)
     }
 
     hfreeentries(hv);
-    xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */
-    xhv->xhv_keys = 0; /* HvKEYS(hv) = 0 */
     xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
     if (xhv->xhv_array /* HvARRAY(hv) */)
        (void)memzero(xhv->xhv_array /* HvARRAY(hv) */,
@@ -1758,6 +1756,12 @@ S_hfreeentries(pTHX_ HV *hv)
     riter = 0;
     max = HvMAX(hv);
     array = HvARRAY(hv);
+    /* make everyone else think the array is empty, so that the destructors
+     * called for freed entries can't recusively mess with us */
+    HvARRAY(hv) = Null(HE**); 
+    HvFILL(hv) = 0;
+    ((XPVHV*) SvANY(hv))->xhv_keys = 0;
+
     entry = array[0];
     for (;;) {
        if (entry) {
@@ -1771,6 +1775,7 @@ S_hfreeentries(pTHX_ HV *hv)
            entry = array[riter];
        }
     }
+    HvARRAY(hv) = array;
     (void)hv_iterinit(hv);
 }
 
@@ -1799,8 +1804,6 @@ Perl_hv_undef(pTHX_ HV *hv)
     }
     xhv->xhv_max   = 7;        /* HvMAX(hv) = 7 (it's a normal hash) */
     xhv->xhv_array = 0;        /* HvARRAY(hv) = 0 */
-    xhv->xhv_fill  = 0;        /* HvFILL(hv) = 0 */
-    xhv->xhv_keys  = 0;        /* HvKEYS(hv) = 0 */
     xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
 
     if (SvRMAGICAL(hv))
index 1d16994..04cac52 100755 (executable)
@@ -5,7 +5,7 @@ BEGIN {
     @INC = '../lib';
 }
 
-print "1..28\n";
+print "1..36\n";
 
 print defined($a) ? "not ok 1\n" : "ok 1\n";
 
@@ -85,3 +85,20 @@ print $@ =~ /^Modification of a read/ ? "ok 23\n" : "not ok 23\n";
     eval 'undef tcp';
     print $@ =~ /^Can't modify constant item/ ? "ok 28\n" : "not ok 28\n";
 }
+
+# bugid 3096
+# undefing a hash may free objects with destructors that then try to
+# modify the hash. To them, the hash should appear empty.
+
+$test = 29;
+%hash = (
+    key1 => bless({}, 'X'),
+    key2 => bless({}, 'X'),
+);
+undef %hash;
+sub X::DESTROY {
+    print "not " if keys   %hash; print "ok $test\n"; $test++;
+    print "not " if values %hash; print "ok $test\n"; $test++;
+    print "not " if each   %hash; print "ok $test\n"; $test++;
+    print "not " if defined delete $hash{'key2'}; print "ok $test\n"; $test++;
+}