Make sure all the XS methods die when called as a class method, and test for this
[gitmo/Class-MOP.git] / MOP.xs
diff --git a/MOP.xs b/MOP.xs
index 9f2476b..ae34df5 100644 (file)
--- a/MOP.xs
+++ b/MOP.xs
@@ -2,13 +2,24 @@
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
+
+#define NEED_sv_2pv_flags
+#define NEED_sv_2pv_nolen
 #include "ppport.h"
 
-/*
-check_method_cache_flag:
-  check the PL_sub_generation 
-  ISA/method cache thing
+SV *key_name;
+U32 hash_name;
+
+SV *key_package;
+U32 hash_package;
+
+SV *key_package_name;
+U32 hash_package_name;
+
+SV *key_body;
+U32 hash_body;
 
+/*
 get_code_info:
   Pass in a coderef, returns:
   [ $pkg_name, $coderef_name ] ie:
@@ -17,12 +28,20 @@ get_code_info:
 
 MODULE = Class::MOP   PACKAGE = Class::MOP
 
-SV*
-check_package_cache_flag()
-  CODE:
-    RETVAL = newSViv(PL_sub_generation);
-  OUTPUT:
-    RETVAL
+BOOT:
+    key_name = newSVpvs("name");
+    key_body = newSVpvs("body");
+    key_package = newSVpvs("package");
+    key_package_name = newSVpvs("package_name");
+
+    PERL_HASH(hash_name, "name", 4);
+    PERL_HASH(hash_body, "body", 4);
+    PERL_HASH(hash_package, "package", 7);
+    PERL_HASH(hash_package_name, "package_name", 12);
+
+
+PROTOTYPES: ENABLE
+
 
 void
 get_code_info(coderef)
@@ -31,14 +50,197 @@ get_code_info(coderef)
     char* name;
     char* pkg;
   PPCODE:
-
     if( SvOK(coderef) && SvROK(coderef) && SvTYPE(SvRV(coderef)) == SVt_PVCV){
       coderef = SvRV(coderef);
-      name    = GvNAME( CvGV(coderef) );
-      pkg     = HvNAME( GvSTASH(CvGV(coderef)) );
+      /* I think this only gets triggered with a mangled coderef, but if
+         we hit it without the guard, we segfault. The slightly odd return
+         value strikes me as an improvement (mst)
+      */
+#ifdef isGV_with_GP
+      if ( isGV_with_GP(CvGV(coderef))) {
+#endif
+        pkg     = HvNAME( GvSTASH(CvGV(coderef)) );
+        name    = GvNAME( CvGV(coderef) );
+#ifdef isGV_with_GP
+      } else {
+        pkg     = "__UNKNOWN__";
+        name    = "__ANON__";
+      }
+#endif
 
       EXTEND(SP, 2);
       PUSHs(newSVpvn(pkg, strlen(pkg)));
       PUSHs(newSVpvn(name, strlen(name)));
     }
 
+
+MODULE = Class::MOP   PACKAGE = Class::MOP::Package
+
+void
+get_all_package_symbols(self, ...)
+    SV *self
+    PROTOTYPE: $;$
+    PREINIT:
+        HV *stash = NULL;
+        SV *type_filter = NULL;
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call get_all_package_symbols as a class method");
+        }
+
+        switch ( GIMME_V ) {
+            case G_VOID: return; break;
+            case G_SCALAR: ST(0) = &PL_sv_undef; return; break;
+        }
+
+        if ( items > 1 ) type_filter = ST(1);
+
+        PUTBACK;
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_package, 0, hash_package))
+            stash = gv_stashsv(HeVAL(he),0);
+
+        if ( stash ) {
+
+            (void)hv_iterinit(stash);
+
+            if ( type_filter && SvPOK(type_filter) ) {
+                const char *const type = SvPV_nolen(type_filter);
+
+                while ((he = hv_iternext(stash))) {
+                    SV *const gv = HeVAL(he);
+                    SV *sv;
+                    char *key;
+                    STRLEN keylen;
+                    char *package;
+                    SV *fq;
+
+                    switch( SvTYPE(gv) ) {
+                        case SVt_PVGV:
+                            switch (*type) {
+                                case 'C': sv = (SV *)GvCVu(gv); break; /* CODE */
+                                case 'A': sv = (SV *)GvAV(gv); break; /* ARRAY */
+                                case 'I': sv = (SV *)GvIO(gv); break; /* IO */
+                                case 'H': sv = (SV *)GvHV(gv); break; /* HASH */
+                                case 'S': sv = (SV *)GvSV(gv); break; /* SCALAR */
+                                default:
+                                          croak("Unknown type %s\n", type);
+                            }
+                            break;
+                        case SVt_RV:
+                            /* BAH! constants are horrible */
+
+                            /* we don't really care about the length,
+                               but that's the API */
+                            key = HePV(he, keylen);
+                            package = HvNAME(stash);
+                            fq = newSVpvf("%s::%s", package, key);
+                            sv = sv_2mortal((SV*)get_cv(SvPV_nolen(fq), 0));
+                            break;
+                        default:
+                            continue;
+                    }
+
+                    if ( sv ) {
+                        SV *key = hv_iterkeysv(he);
+                        SPAGAIN;
+                        EXTEND(SP, 2);
+                        PUSHs(key);
+                        PUSHs(sv_2mortal(newRV_inc(sv)));
+                        PUTBACK;
+                    }
+                }
+            } else {
+                EXTEND(SP, HvKEYS(stash) * 2);
+
+                while ((he = hv_iternext(stash))) {
+                    SV *key = hv_iterkeysv(he);
+                    SV *sv = HeVAL(he);
+                    SPAGAIN;
+                    PUSHs(key);
+                    PUSHs(sv);
+                    PUTBACK;
+                }
+            }
+
+        }
+
+SV *
+name(self)
+    SV *self
+    PREINIT:
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call name as a class method");
+        }
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_package, 0, hash_package))
+            XPUSHs(HeVAL(he));
+        else
+            ST(0) = &PL_sv_undef;
+
+MODULE = Class::MOP   PACKAGE = Class::MOP::Attribute
+
+SV *
+name(self)
+    SV *self
+    PREINIT:
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call name as a class method");
+        }
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_name, 0, hash_name))
+            XPUSHs(HeVAL(he));
+        else
+            ST(0) = &PL_sv_undef;
+
+MODULE = Class::MOP   PACKAGE = Class::MOP::Method
+
+SV *
+name(self)
+    SV *self
+    PREINIT:
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call name as a class method");
+        }
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_name, 0, hash_name))
+            XPUSHs(HeVAL(he));
+        else
+            ST(0) = &PL_sv_undef;
+
+SV *
+package_name(self)
+    SV *self
+    PREINIT:
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call package_name as a class method");
+        }
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_package_name, 0, hash_package_name))
+            XPUSHs(HeVAL(he));
+        else
+            ST(0) = &PL_sv_undef;
+
+SV *
+body(self)
+    SV *self
+    PREINIT:
+        register HE *he;
+    PPCODE:
+        if (! SvROK(self)) {
+            die("Cannot call body as a class method");
+        }
+
+        if (he = hv_fetch_ent((HV *)SvRV(self), key_body, 0, hash_body))
+            XPUSHs(HeVAL(he));
+        else
+            ST(0) = &PL_sv_undef;