aTHX_ thing
[gitmo/Class-C3-XS.git] / lib / Class / C3 / XS.xs
index 8e004cf..2b3f59d 100644 (file)
@@ -3,11 +3,14 @@
 #include "perl.h"
 #include "XSUB.h"
 
-/* Most of this code is backported from bleadperl's
+/* Most of this code is backported from the bleadperl patch's
    mro.c, and then modified to work with Class::C3's
    internals.
 */
 
+/* This is %next::METHOD_CACHE */
+STATIC HV* nmcache;
+
 AV*
 __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level)
 {
@@ -32,10 +35,12 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level)
               stashname);
 
     if(!cache) {
-        cache = newHV();
+        cache = (HV*)sv_2mortal((SV*)newHV());
     }
     else {
-        /* XXX return cached entry for stashname if available */
+        SV** cache_entry = hv_fetch(cache, stashname, stashname_len, 0);
+        if(cache_entry)
+            return (AV*)SvREFCNT_inc(*cache_entry);
     }
 
     /* not in cache, make a new one */
@@ -62,7 +67,7 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level)
                 av_push(isa_lin, newSVsv(isa_item));
             }
             else {
-                isa_lin = (AV*)sv_2mortal((SV*)__mro_linear_isa_c3(isa_item_stash, cache, level + 1)); /* recursion */
+                isa_lin = (AV*)sv_2mortal((SV*)__mro_linear_isa_c3(aTHX_ isa_item_stash, cache, level + 1)); /* recursion */
             }
             av_push(seqs, (SV*)av_make(AvFILLp(isa_lin)+1, AvARRAY(isa_lin)));
         }
@@ -139,8 +144,8 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level)
 
     SvREADONLY_on(retval);
     SvREFCNT_inc(retval); /* for cache storage */
-    SvREFCNT_inc(retval); /* for return to caller */
-    /* XXX store in cache storage */
+    SvREFCNT_inc(retval); /* for return */
+    hv_store(cache, stashname, stashname_len, (SV*)retval, 0);
     return retval;
 }
 
@@ -177,9 +182,8 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
     CV* cand_cv = NULL;
     const char *hvname;
     I32 items;
-    struct mro_meta* selfmeta;
-    HV* nmcache;
     HE* cache_entry;
+    SV* cachekey;
 
     if(sv_isobject(self))
         selfstash = SvSTASH(SvRV(self));
@@ -253,15 +257,12 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
 
     /* If we made it to here, we found our context */
 
-    /* 
-    XXX check %next::METHOD_CACHE 
-
-    selfmeta = HvMROMETA(selfstash);
-    if(!(nmcache = selfmeta->mro_nextmethod)) {
-        nmcache = selfmeta->mro_nextmethod = newHV();
-    }
+    /* cachekey = "objpkg|context::method::name" */
+    cachekey = sv_2mortal(newSVpv(hvname, 0));
+    sv_catpvn(cachekey, "|", 1);
+    sv_catsv(cachekey, sv);
 
-    if((cache_entry = hv_fetch_ent(nmcache, sv, 0, 0))) {
+    if((cache_entry = hv_fetch_ent(nmcache, cachekey, 0, 0))) {
         SV* val = HeVAL(cache_entry);
         if(val == &PL_sv_undef) {
             if(throw_nomethod)
@@ -270,15 +271,13 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
         }
         return SvREFCNT_inc(val);
     }
-    */
 
     /* beyond here is just for cache misses, so perf isn't as critical */
 
     stashname_len = subname - fq_subname - 2;
     stashname = sv_2mortal(newSVpvn(fq_subname, stashname_len));
 
-    linear_av = __mro_linear_isa_c3(selfstash, NULL, 0); /* has ourselves at the top of the list */
-    sv_2mortal((SV*)linear_av);
+    linear_av = (AV*)sv_2mortal((SV*)__mro_linear_isa_c3(aTHX_ selfstash, NULL, 0));
 
     linear_svp = AvARRAY(linear_av);
     items = AvFILLp(linear_av) + 1;
@@ -291,9 +290,26 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
     }
 
     if(items > 0) {
+        SV* sub_sv = sv_2mortal(newSVpv(subname, subname_len));
+        HV* cc3_mro = get_hv("Class::C3::MRO", 0);
+
         while (items--) {
             linear_sv = *linear_svp++;
             assert(linear_sv);
+
+            if(cc3_mro) {
+                HE* he_cc3_mro_class = hv_fetch_ent(cc3_mro, linear_sv, 0, 0);
+                if(he_cc3_mro_class) {
+                    HV* cc3_mro_class = (HV*)SvRV(HeVAL(he_cc3_mro_class));
+                    SV** svp_cc3_mro_class_methods = hv_fetch(cc3_mro_class, "methods", 7, 0);
+                    if(svp_cc3_mro_class_methods) {
+                        HV* cc3_mro_class_methods = (HV*)SvRV(*svp_cc3_mro_class_methods);
+                        if(hv_exists_ent(cc3_mro_class_methods, sub_sv, 0))
+                            continue;
+                    }
+                }
+            }
+
             cstash = gv_stashsv(linear_sv, FALSE);
 
             if (!cstash) {
@@ -305,8 +321,6 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
 
             assert(cstash);
 
-/* XXX we need to fetch from %Class::C3::MRO, not the real stash table */
-
             gvp = (GV**)hv_fetch(cstash, subname, subname_len, 0);
             if (!gvp) continue;
 
@@ -317,15 +331,13 @@ __nextcan(pTHX_ SV* self, I32 throw_nomethod)
                 gv_init(candidate, cstash, subname, subname_len, TRUE);
             if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) {
                 SvREFCNT_inc((SV*)cand_cv);
-                /* XXX store result in cache */
-                /* hv_store_ent(nmcache, newSVsv(sv), (SV*)cand_cv, 0); */
+                hv_store_ent(nmcache, newSVsv(cachekey), (SV*)cand_cv, 0);
                 return (SV*)cand_cv;
             }
         }
     }
 
-    /* XXX store undef in cache */
-    /* hv_store_ent(nmcache, newSVsv(sv), &PL_sv_undef, 0); */
+    hv_store_ent(nmcache, newSVsv(cachekey), &PL_sv_undef, 0);
     if(throw_nomethod)
         croak("No next::method '%s' found for %s", subname, hvname);
     return &PL_sv_undef;
@@ -352,13 +364,12 @@ XS(XS_Class_C3_XS_calculateMRO)
         croak("Usage: calculateMRO(classname[, cache])");
 
     classname = ST(0);
-    if(items == 2) cache = (HV*)ST(1);
+    if(items == 2) cache = (HV*)SvRV(ST(1));
 
     class_stash = gv_stashsv(classname, 0);
     if(!class_stash) croak("No such class: '%s'!", SvPV_nolen(classname));
 
-    res = (AV*)sv_2mortal((SV*)__mro_linear_isa_c3(class_stash, cache, 0));
-
+    res = (AV*)sv_2mortal((SV*)__mro_linear_isa_c3(aTHX_ class_stash, cache, 0));
 
     res_items = ret_items = AvFILLp(res) + 1;
     res_ptr = AvARRAY(res);
@@ -385,7 +396,7 @@ XS(XS_next_can)
 #endif
 
     SV* self = ST(0);
-    SV* methcv = __nextcan(self, 0);
+    SV* methcv = __nextcan(aTHX_ self, 0);
 
     PERL_UNUSED_VAR(items);
 
@@ -405,7 +416,7 @@ XS(XS_next_method)
     dMARK;
     dAX;
     SV* self = ST(0);
-    SV* methcv = __nextcan(self, 1);
+    SV* methcv = __nextcan(aTHX_ self, 1);
 
     PL_markstack_ptr++;
     call_sv(methcv, GIMME_V);
@@ -417,7 +428,7 @@ XS(XS_maybe_next_method)
     dMARK;
     dAX;
     SV* self = ST(0);
-    SV* methcv = __nextcan(self, 0);
+    SV* methcv = __nextcan(aTHX_ self, 0);
 
     if(methcv == &PL_sv_undef) {
         ST(0) = &PL_sv_undef;
@@ -431,6 +442,7 @@ XS(XS_maybe_next_method)
 MODULE = Class::C3::XS PACKAGE = Class::C3::XS
 
 BOOT:
+    nmcache = get_hv("next::METHOD_CACHE", 1);
     newXS("Class::C3::XS::calculateMRO", XS_Class_C3_XS_calculateMRO, __FILE__);
     newXS("next::can", XS_next_can, __FILE__);
     newXS("next::method", XS_next_method, __FILE__);