Refactor XS metaclass object structure
gfx [Wed, 4 Nov 2009 06:57:36 +0000 (15:57 +0900)]
mouse.h
xs-src/Mouse.xs
xs-src/MouseAccessor.xs
xs-src/MouseUtil.xs

diff --git a/mouse.h b/mouse.h
index 662e227..a7a5986 100644 (file)
--- a/mouse.h
+++ b/mouse.h
@@ -72,9 +72,18 @@ bool mouse_is_class_loaded(pTHX_ SV*);
 #define mcall0s(invocant, m)       mcall0((invocant), sv_2mortal(newSVpvs_share(m)))
 #define mcall1s(invocant, m, arg1) mcall1((invocant), sv_2mortal(newSVpvs_share(m)), (arg1))
 
+#define get_metaclass_by_name(name) mouse_get_metaclass_by_name(aTHX_ name)
+
 SV* mouse_call0(pTHX_ SV *const self, SV *const method);
 SV* mouse_call1(pTHX_ SV *const self, SV *const method, SV* const arg1);
 
+SV* mouse_get_metaclass_by_name(pTHX_ SV* const metaclass_name);
+
+GV* mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create);
+#define stash_fetch(s, n, l, c) mouse_stash_fetch(aTHX_ (s), (n), (l), (c))
+#define stash_fetchs(s, n, c)   mouse_stash_fetch(aTHX_ (s), STR_WITH_LEN(n), (c))
+
+
 #define MOUSEf_DIE_ON_FAIL 0x01
 MAGIC* mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags);
 
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
index b6c11cc..d2ae129 100644 (file)
@@ -6,8 +6,10 @@
         }                                                             \
     } STMT_END
 
-/* Moose XS Attribute object */
+/* Mouse XS Attribute object */
 enum mouse_xa_ix_t{
+    MOUSE_XA_SLOT,      /* for constructors, sync to mg_obj */
+    MOUSE_XA_FLAGS,     /* for constructors, sync to mg_private */
     MOUSE_XA_ATTRIBUTE,
     MOUSE_XA_TC,
     MOUSE_XA_TC_CODE,
@@ -25,7 +27,7 @@ enum mouse_xa_flags_t{
     MOUSEf_ATTR_HAS_TC          = 0x0001,
     MOUSEf_ATTR_HAS_DEFAULT     = 0x0002,
     MOUSEf_ATTR_HAS_BUILDER     = 0x0004,
-    MOUSEf_ATTR_HAS_INITIALIZER = 0x0008, /* not used in Mouse */
+    MOUSEf_ATTR_HAS_INITIALIZER = 0x0008,
     MOUSEf_ATTR_HAS_TRIGGER     = 0x0010,
 
     MOUSEf_ATTR_IS_LAZY         = 0x0020,
index d47ed8b..2e7f50b 100644 (file)
@@ -196,6 +196,25 @@ mouse_call1 (pTHX_ SV *const self, SV *const method, SV* const arg1)
     return ret;
 }
 
+SV*
+mouse_get_metaclass_by_name(pTHX_ SV* const metaclass_name){
+    CV* const get_metaclass  = get_cvs("Mouse::Util::get_metaclass_by_name", TRUE);
+    SV* metaclass;
+    dSP;
+
+    PUSHMARK(SP);
+    XPUSHs(metaclass_name);
+    PUTBACK;
+
+    call_sv((SV*)get_metaclass, G_SCALAR);
+
+    SPAGAIN;
+    metaclass = POPs;
+    PUTBACK;
+
+    return metaclass;
+}
+
 MAGIC*
 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
     MAGIC* mg;
@@ -213,6 +232,21 @@ mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
     return NULL;
 }
 
+GV*
+mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
+    GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
+
+    if(gvp){
+        if(!isGV(*gvp)){
+            gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
+        }
+        return *gvp;
+    }
+    else{
+        return NULL;
+    }
+}
+
 MODULE = Mouse::Util  PACKAGE = Mouse::Util
 
 PROTOTYPES:   DISABLE
@@ -252,7 +286,9 @@ get_code_ref(SV* package, SV* name)
 CODE:
 {
     HV* stash;
-    HE* he;
+    STRLEN name_len;
+    const char* name_pv;
+    GV* gv;
 
     if(!SvOK(package)){
         croak("You must define a package name");
@@ -265,19 +301,10 @@ CODE:
     if(!stash){
         XSRETURN_UNDEF;
     }
-    he = hv_fetch_ent(stash, name, FALSE, 0U);
-    if(he){
-        GV* const gv = (GV*)hv_iterval(stash, he);
-        if(!isGV(gv)){ /* special constant or stub */
-            STRLEN len;
-            const char* const pv = SvPV_const(name, len);
-            gv_init(gv, stash, pv, len, GV_ADDMULTI);
-        }
-        RETVAL = GvCVu(gv);
-    }
-    else{
-        RETVAL = NULL;
-    }
+
+    name_pv = SvPV_const(name, name_len);
+    gv = stash_fetch(stash, name_pv, name_len, FALSE);
+    RETVAL = gv ? GvCVu(gv) : NULL;
 
     if(!RETVAL){
         XSRETURN_UNDEF;