Refactor XS metaclass object structure
[gitmo/Mouse.git] / xs-src / Mouse.xs
index 4783961..0521e1d 100644 (file)
@@ -9,97 +9,156 @@ SV* mouse_name;
 static SV* mouse_all_attrs_cache;
 static SV* mouse_all_attrs_cache_gen;
 
-AV*
-mouse_get_all_attributes(pTHX_ SV* const metaclass){
-    SV* const package = get_slot(metaclass, mouse_package);
-    HV* const stash   = gv_stashsv(package, TRUE);
-    UV const pkg_gen  = mro_get_pkg_gen(stash);
-    SV* cache_gen     = get_slot(metaclass, mouse_all_attrs_cache_gen);
+#define MOUSE_xc_gen(a)         MOUSE_av_at((a), MOUSE_XC_GEN)
+#define MOUSE_xc_attrall(a)     ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) )
+#define MOUSE_xc_buildall(a)    ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) )
+#define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEOLISHALL) )
+
+/* Mouse XS Metaclass object */
+enum mouse_xc_ix_t{
+    MOUSE_XC_GEN,          /* class generation */
+    MOUSE_XC_ATTRALL,      /* all the attributes */
+    MOUSE_XC_BUILDALL,     /* all the BUILD methods */
+    MOUSE_XC_DEMOLISHALL,  /* all the DEMOLISH methods */
+
+    MOUSE_XC_last
+};
+
+static MGVTBL mouse_xc_vtbl; /* for identity */
+
+static void
+mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){
+    dSP;
+    I32 n;
+
+    /* $meta->get_attribute_list */
+    PUSHMARK(SP);
+    XPUSHs(metaclass);
+    PUTBACK;
+
+    n = call_method("get_attribute_list", G_ARRAY);
+    for(NOOP; n > 0; n--){
+        SV* name;
+
+        SPAGAIN;
+        name = POPs;
+        PUTBACK;
+
+        if(hv_exists_ent(seen, name, 0U)){
+            continue;
+        }
+        (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
 
-    if(!(cache_gen && pkg_gen == SvUV(cache_gen))){ /* update */
-        CV* const get_metaclass  = get_cvs("Mouse::Util::get_metaclass_by_name", TRUE);
-        AV* const all_attrs      = newAV();
-        SV* const get_attribute  = newSVpvs_share("get_attribute");
+        av_push(attrall, newSVsv( mcall1s(metaclass, "get_attribute", name) ));
+    }
+}
 
-        AV* const linearized_isa = mro_get_linear_isa(stash);
-        I32 const len            = AvFILLp(linearized_isa);
-        I32 i;
-        HV* seen;
+static void
+mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) {
+    AV* const linearized_isa = mro_get_linear_isa(stash);
+    I32 const len            = AvFILLp(linearized_isa);
+    I32 i;
+    AV* const attrall     = newAV();
+    AV* const buildall    = newAV();
+    AV* const demolishall = newAV();
+    HV* const seen        = newHV(); /* for attributes */
 
-        /* warn("Update all_attrs_cache (cache_gen %d != pkg_gen %d)", (cache_gen ? (int)SvIV(cache_gen) : 0), (int)pkg_gen); //*/
+    ENTER;
+    SAVETMPS;
 
-        ENTER;
-        SAVETMPS;
+    sv_2mortal((SV*)seen);
 
-        sv_2mortal(get_attribute);
+     /* old data will be delete at the end of the perl scope */
+    av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00);
+    av_delete(xc, MOUSE_XC_BUILDALL,    0x00);
+    av_delete(xc, MOUSE_XC_ATTRALL,     0x00);
 
-        set_slot(metaclass, mouse_all_attrs_cache, sv_2mortal(newRV_inc((SV*)all_attrs)));
+    SvREFCNT_inc_simple_void_NN(linearized_isa);
+    sv_2mortal((SV*)linearized_isa);
 
-        seen = newHV();
-        sv_2mortal((SV*)seen);
+    /* update */
 
-        for(i = 0; i < len; i++){
-            SV* const klass = MOUSE_av_at(linearized_isa, i);
-            SV* meta;
-            I32 n;
-            dSP;
+    av_store(xc, MOUSE_XC_ATTRALL,     (SV*)attrall);
+    av_store(xc, MOUSE_XC_BUILDALL,    (SV*)buildall);
+    av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
 
-            PUSHMARK(SP);
-            XPUSHs(klass);
-            PUTBACK;
+    for(i = 0; i < len; i++){
+        SV* const klass     = MOUSE_av_at(linearized_isa, i);
+        SV* meta;
+        GV* gv;
 
-            call_sv((SV*)get_metaclass, G_SCALAR);
+        gv = stash_fetchs(stash, "BUILD", FALSE);
+        if(gv && GvCVu(gv)){
+            av_push(buildall, newRV_inc((SV*)GvCV(gv)));
+        }
 
-            SPAGAIN;
-            meta = POPs;
-            PUTBACK;
+        gv = stash_fetchs(stash, "DEMOLISH", FALSE);
+        if(gv && GvCVu(gv)){
+            av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
+        }
 
-            if(!SvOK(meta)){
-                continue; /* skip non-Mouse classes */
-            }
+        /* ATTRIBUTES */
+        meta = get_metaclass_by_name(klass);
+        if(!SvOK(meta)){
+            continue; /* skip non-Mouse classes */
+        }
 
-            /* $meta->get_attribute_list */
-            PUSHMARK(SP);
-            XPUSHs(meta);
-            PUTBACK;
+        mouse_class_push_attribute_list(aTHX_ meta, attrall, seen);
+    }
 
-            n = call_method("get_attribute_list", G_ARRAY);
-            for(NOOP; n > 0; n--){
-                SV* name;
+    FREETMPS;
+    LEAVE;
 
-                SPAGAIN;
-                name = POPs;
-                PUTBACK;
+    sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash));
+}
 
-                if(hv_exists_ent(seen, name, 0U)){
-                    continue;
-                }
-                (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
+AV*
+mouse_get_xc(pTHX_ SV* const metaclass) {
+    AV* xc;
+    SV* gen;
+    HV* stash;
+    MAGIC* mg;
+
+    if(!IsObject(metaclass)){
+        croak("Not a Mouse metaclass");
+    }
 
-                av_push(all_attrs, newSVsv( mcall1(meta, get_attribute, name) ));
-            }
-        }
+    mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00);
+    if(!mg){
+        SV* const package = get_slot(metaclass, mouse_package);
 
-        if(!cache_gen){
-            cache_gen = sv_newmortal();
-        }
-        sv_setuv(cache_gen, mro_get_pkg_gen(stash));
-        set_slot(metaclass, mouse_all_attrs_cache_gen, cache_gen);
+        stash = gv_stashsv(package, TRUE);
+        xc    = newAV();
 
-        FREETMPS;
-        LEAVE;
+        mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, &mouse_xc_vtbl, (char*)stash, HEf_SVKEY);
+        SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */
 
-        return all_attrs;
+        av_extend(xc, MOUSE_XC_last - 1);
+        av_store(xc, MOUSE_XC_GEN, newSViv(0));
     }
-    else {
-        SV* const all_attrs_ref = get_slot(metaclass, mouse_all_attrs_cache);
+    else{
+        stash = (HV*)MOUSE_mg_ptr(mg);
+        xc    = (AV*)MOUSE_mg_obj(mg);
 
-        if(!IsArrayRef(all_attrs_ref)){
-            croak("Not an ARRAY reference");
-        }
+        assert(stash);
+        assert(SvTYPE(stash) == SVt_PVAV);
 
-        return (AV*)SvRV(all_attrs_ref);
+        assert(xc);
+        assert(SvTYPE(xc) == SVt_PVAV);
     }
+
+    gen = MOUSE_xc_gen(xc);
+    if(SvUV(gen) != mro_get_pkg_gen(stash)){
+        mouse_class_update_xc(aTHX_ metaclass, stash, xc);
+    }
+
+    return xc;
+}
+
+AV*
+mouse_get_all_attributes(pTHX_ SV* const metaclass) {
+    AV* const xc = mouse_get_xc(aTHX_ metaclass);
+    return MOUSE_xc_attrall(xc);
 }
 
 MODULE = Mouse  PACKAGE = Mouse