make the namespace cache lazy and weak, in case the stash is deleted
Jesse Luehrs [Fri, 4 Mar 2011 18:33:39 +0000 (12:33 -0600)]
XS.xs
t/07-edge-cases.t

diff --git a/XS.xs b/XS.xs
index 159aa5a..73ca16f 100644 (file)
--- a/XS.xs
+++ b/XS.xs
@@ -351,8 +351,6 @@ new(class, package_name)
     SV *package_name
   PREINIT:
     HV *instance;
-    HV *namespace;
-    SV *nsref;
   CODE:
     if (!SvPOK(package_name))
         croak("The constructor argument must be the name of a package");
@@ -364,13 +362,6 @@ new(class, package_name)
         SvREFCNT_dec(instance);
         croak("Couldn't initialize the 'name' key, hv_store failed");
     }
-    namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
-    nsref = newRV_inc((SV*)namespace);
-    if (!hv_store(instance, "namespace", 9, nsref, 0)) {
-        SvREFCNT_dec(nsref);
-        SvREFCNT_dec(instance);
-        croak("Couldn't initialize the 'namespace' key, hv_store failed");
-    }
 
     RETVAL = sv_bless(newRV_noinc((SV*)instance), gv_stashsv(class, 0));
   OUTPUT:
@@ -398,7 +389,24 @@ namespace(self)
     if (!sv_isobject(self))
         croak("Can't call namespace as a class method");
     slot = hv_fetch_ent((HV*)SvRV(self), namespace_key, 0, namespace_hash);
-    RETVAL = slot ? SvREFCNT_inc_simple_NN(HeVAL(slot)) : &PL_sv_undef;
+    if (slot) {
+        RETVAL = SvREFCNT_inc_simple_NN(HeVAL(slot));
+    }
+    else {
+        HV *namespace;
+        SV *nsref, *package_name;
+
+        package_name = _get_name(self);
+        namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
+        nsref = newRV_inc((SV*)namespace);
+        sv_rvweaken(nsref);
+        if (!hv_store((HV*)SvRV(self), "namespace", 9, nsref, 0)) {
+            SvREFCNT_dec(nsref);
+            SvREFCNT_dec(self);
+            croak("Couldn't initialize the 'namespace' key, hv_store failed");
+        }
+        RETVAL = SvREFCNT_inc_simple_NN(nsref);
+    }
   OUTPUT:
     RETVAL
 
index 4f61ca2..acd92e8 100755 (executable)
@@ -66,4 +66,19 @@ is(exception { $Bar->add_symbol('$bar', \$bar) }, undef,
 
 use_ok('CompileTime');
 
+{
+    package Gets::Deleted;
+    sub bar { }
+}
+
+{
+    my $delete = Package::Stash->new('Gets::Deleted');
+    ok($delete->has_symbol('&bar'), "sees the method");
+    {
+        no strict 'refs';
+        delete ${'main::Gets::'}{'Deleted::'};
+    }
+    ok(!$delete->has_symbol('&bar'), "method goes away when stash is deleted");
+}
+
 done_testing;