Revert "Merge branch 'master' into topic/symbol-manipulator"
[gitmo/Class-MOP.git] / xs / Package.xs
index 17838ed..362c407 100644 (file)
@@ -1,97 +1,80 @@
-#define NEED_newSVpvn_flags
 #include "mop.h"
 
-static SV*
-mop_deconstruct_variable_name(pTHX_ SV* const variable, svtype* const type, const char** const type_name) {
-       SV* name;
-
-       if(SvROK(variable) && SvTYPE(SvRV(variable)) == SVt_PVHV){
-               /* e.g. variable = { type => "SCALAR", name => "foo" } */
-               HV* const hv = (HV*)SvRV(variable);
-               SV** svp;
-               STRLEN len;
-               const char* pv;
+static void
+mop_update_method_map(pTHX_ SV *const self, SV *const class_name, HV *const stash, HV *const map)
+{
+    const char *const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */
+    SV   *method_metaclass_name;
+    char *method_name;
+    I32   method_name_len;
+    SV   *coderef;
+    HV   *symbols;
+    dSP;
+
+    symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE);
+    sv_2mortal((SV*)symbols);
+    (void)hv_iterinit(symbols);
+    while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) {
+        CV *cv = (CV *)SvRV(coderef);
+        char *cvpkg_name;
+        char *cv_name;
+        SV *method_slot;
+        SV *method_object;
+
+        if (!mop_get_code_info(coderef, &cvpkg_name, &cv_name)) {
+            continue;
+        }
 
-               svp = hv_fetchs(hv, "name", FALSE);
-               if(!(svp && SvOK(*svp))){
-                       croak("You must pass a variable name");
-               }
-               name = *svp;
-               pv   = SvPV_const(name, len);
-               if(len < 1){
-                       croak("You must pass a variable name");
-               }
+        /* this checks to see that the subroutine is actually from our package  */
+        if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) {
+            if ( strNE(cvpkg_name, class_name_pv) ) {
+                continue;
+            }
+        }
 
-               svp = hv_fetchs(hv, "type", FALSE);
-               if(!(svp && SvOK(*svp))) {
-                       croak("You must pass a variable type");
-               }
-               pv = SvPV_nolen_const(*svp);
-               if(strEQ(pv, "SCALAR")){
-                       *type = SVt_PV; /* for all the type of scalars */
-               }
-               else if(strEQ(pv, "ARRAY")){
-                       *type = SVt_PVAV;
-               }
-               else if(strEQ(pv, "HASH")){
-                       *type = SVt_PVHV;
-               }
-               else if(strEQ(pv, "CODE")){
-                       *type = SVt_PVCV;
-               }
-               else if(strEQ(pv, "GLOB")){
-                       *type = SVt_PVGV;
-               }
-               else if(strEQ(pv, "IO")){
-                       *type = SVt_PVIO;
-               }
-               else{
-                       croak("I do not recognize that type '%s'", pv);
-               }
-               *type_name = pv;
-       }
-       else {
-               STRLEN len;
-               const char* pv;
-               /* e.g. variable = '$foo' */
-               if(!SvOK(variable)) {
-                       croak("You must pass a variable name");
-               }
-               pv = SvPV_const(variable, len);
-               if(len < 2){
-                       croak("You must pass a variable name including a sigil");
-               }
+        method_slot = *hv_fetch(map, method_name, method_name_len, TRUE);
+        if ( SvOK(method_slot) ) {
+            SV *const body = mop_call0(aTHX_ method_slot, KEY_FOR(body)); /* $method_object->body() */
+            if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) {
+                continue;
+            }
+        }
 
-               switch(pv[0]){
-               case '$':
-                       *type      = SVt_PV; /* for all the types of scalars */
-                       *type_name = "SCALAR";
-                       break;
-               case '@':
-                       *type      = SVt_PVAV;
-                       *type_name = "ARRAY";
-                       break;
-               case '%':
-                       *type      = SVt_PVHV;
-                       *type_name = "HASH";
-                       break;
-               case '&':
-                       *type      = SVt_PVCV;
-                       *type_name = "CODE";
-                       break;
-               case '*':
-                       *type      = SVt_PVGV;
-                       *type_name = "GLOB";
-                       break;
-               default:
-                       croak("I do not recognize that sigil '%c'", pv[0]);
-               }
+        method_metaclass_name = mop_call0(aTHX_ self, mop_method_metaclass); /* $self->method_metaclass() */
+
+        /*
+            $method_object = $method_metaclass->wrap(
+                $cv,
+                associated_metaclass => $self,
+                package_name         => $class_name,
+                name                 => $method_name
+            );
+        */
+        ENTER;
+        SAVETMPS;
+
+        PUSHMARK(SP);
+        EXTEND(SP, 8);
+        PUSHs(method_metaclass_name); /* invocant */
+        mPUSHs(newRV_inc((SV *)cv));
+        PUSHs(mop_associated_metaclass);
+        PUSHs(self);
+        PUSHs(KEY_FOR(package_name));
+        PUSHs(class_name);
+        PUSHs(KEY_FOR(name));
+        mPUSHs(newSVpv(method_name, method_name_len));
+        PUTBACK;
 
-               name = newSVpvn_share(pv+1, len-1, 0U);
-               sv_2mortal(name);
-       }
+        call_sv(mop_wrap, G_SCALAR | G_METHOD);
+        SPAGAIN;
+        method_object = POPs;
+        PUTBACK;
+        /* $map->{$method_name} = $method_object */
+        sv_setsv(method_slot, method_object);
 
-       return name;
+        FREETMPS;
+        LEAVE;
+    }
 }
 
 MODULE = Class::MOP::Package   PACKAGE = Class::MOP::Package
@@ -129,149 +112,39 @@ get_all_package_symbols(self, filter=TYPE_FILTER_NONE)
         symbols = mop_get_all_package_symbols(stash, filter);
         PUSHs(sv_2mortal(newRV_noinc((SV *)symbols)));
 
-BOOT:
-    INSTALL_SIMPLE_READER_WITH_KEY(Package, name, package);
-
-SV*
-add_package_symbol(SV* self, SV* variable, SV* ref = &PL_sv_undef)
-PREINIT:
-       svtype type;
-       const char* type_name;
-       SV* var_name;
-       SV* package_name;
-       SV* fq_name;
-CODE:
-       var_name = mop_deconstruct_variable_name(aTHX_ variable, &type, &type_name);
-
-       package_name = mop_call0(aTHX_ self, KEY_FOR(name));
-       if(!SvOK(package_name)){
-               croak("name() did not return a defined value");
-       }
-       fq_name = newSVpvf("%"SVf"::%"SVf, package_name, var_name);
-       sv_2mortal(fq_name);
-
-       if(SvOK(ref)){ /* set */
-               GV* gv;
-               if(type == SVt_PV){
-                       if(!SvROK(ref)){
-                               ref = newRV_noinc(newSVsv(ref));
-                               sv_2mortal(ref);
-                       }
-               }
-               else if(!(SvROK(ref) && SvTYPE(SvRV(ref)) == type)){
-                       croak("You must pass a reference of %s for the value of %s", type_name, GvNAME(CvGV(cv)));
-               }
-               gv = gv_fetchsv(fq_name, GV_ADDMULTI, type);
-
-               if(type == SVt_PVCV && GvCV(gv)){
-                       /* XXX: should introduce an option { redefine => 1 } ? */
-                       SvREFCNT_dec(GvCV(gv));
-                       GvCV(gv) = NULL;
-               }
-               sv_setsv_mg((SV*)gv, ref); /* *glob = $ref */
-               RETVAL = ref;
-       }
-       else { /* init */
-               GV* const gv = gv_fetchsv(fq_name, GV_ADDMULTI, type);
-               SV* sv;
-
-               switch(type){
-               case SVt_PV:
-                       sv = GvSVn(gv);
-                       break;
-               case SVt_PVAV:
-                       sv = (SV*)GvAVn(gv);
-                       break;
-               case SVt_PVHV:
-                       sv = (SV*)GvHVn(gv);
-                       break;
-               case SVt_PVCV:
-                       sv = (SV*)GvCV(gv);
-                       break;
-               case SVt_PVGV:
-                       sv = (SV*)gv;
-                       break;
-               case SVt_PVIO:
-                       sv = (SV*)GvIOn(gv);
-                       break;
-               default:
-                       croak("NOT REACHED");
-                       sv = NULL; /* -W */
-                       break;
-               }
-
-               if(sv){
-                       RETVAL = sv_2mortal(newRV_inc(sv));
-               }
-               else{
-                       RETVAL = &PL_sv_undef;
-               }
-       }
-       ST(0) = RETVAL;
+void
+get_method_map(self)
+    SV *self
+    PREINIT:
+        HV *const obj        = (HV *)SvRV(self);
+        SV *const class_name = HeVAL( hv_fetch_ent(obj, KEY_FOR(package), 0, HASH_FOR(package)) );
+        HV *const stash      = gv_stashsv(class_name, 0);
+        UV current;
+        SV *cache_flag;
+        SV *map_ref;
+    PPCODE:
+        if (!stash) {
+             mXPUSHs(newRV_noinc((SV *)newHV()));
+             return;
+        }
 
-SV*
-get_package_symbol(SV* self, SV* variable)
-ALIAS:
-       get_package_symbol = GV_ADDMULTI
-       has_package_symbol = 0
-PREINIT:
-       svtype type;
-       const char* type_name;
-       SV* var_name;
-       SV* package_name;
-       SV* fq_name;
-       GV* gv;
-       SV* sv;
-CODE:
-       var_name = mop_deconstruct_variable_name(aTHX_ variable, &type, &type_name);
+        current    = mop_check_package_cache_flag(aTHX_ stash);
+        cache_flag = HeVAL( hv_fetch_ent(obj, KEY_FOR(package_cache_flag), TRUE, HASH_FOR(package_cache_flag)));
+        map_ref    = HeVAL( hv_fetch_ent(obj, KEY_FOR(methods), TRUE, HASH_FOR(methods)));
 
-       package_name = mop_call0(aTHX_ self, KEY_FOR(name));
-       if(!SvOK(package_name)){
-               croak("name() did not return a defined value");
-       }
-       fq_name = newSVpvf("%"SVf"::%"SVf, package_name, var_name);
-       sv_2mortal(fq_name);
+        /* $self->{methods} does not yet exist (or got deleted) */
+        if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) {
+            SV *new_map_ref = newRV_noinc((SV *)newHV());
+            sv_2mortal(new_map_ref);
+            sv_setsv(map_ref, new_map_ref);
+        }
 
-       gv = gv_fetchsv(fq_name, ix, type);
-       if(!gv){ /* no symbol in has_package_symbol() */
-               XSRETURN_NO;
-       }
+        if ( !SvOK(cache_flag) || SvUV(cache_flag) != current ) {
+            mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref));
+            sv_setuv(cache_flag, mop_check_package_cache_flag(aTHX_ stash)); /* update_cache_flag() */
+        }
 
-       switch(type){
-       case SVt_PV:
-               sv = GvSV(gv);
-               break;
-       case SVt_PVAV:
-               sv = (SV*)GvAV(gv);
-               break;
-       case SVt_PVHV:
-               sv = (SV*)GvHV(gv);
-               break;
-       case SVt_PVCV:
-               sv = (SV*)GvCV(gv);
-               break;
-       case SVt_PVGV:
-               sv = (SV*)gv;
-               break;
-       case SVt_PVIO:
-               sv = (SV*)GvIO(gv);
-               break;
-       default:
-               croak("NOT REACHED");
-               sv = NULL; /* -W */
-               break;
-       }
+        XPUSHs(map_ref);
 
-       if(!ix){ /* has_package_symbol */
-               RETVAL = boolSV(sv);
-       }
-       else{
-               if(sv){
-                       RETVAL = newRV_inc(sv);
-               }
-               else{
-                       RETVAL = &PL_sv_undef;
-               }
-       }
-OUTPUT:
-       RETVAL
+BOOT:
+    INSTALL_SIMPLE_READER_WITH_KEY(Package, name, package);