This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Lies, damn lies and end-of-block comments
[perl5.git] / mro.c
diff --git a/mro.c b/mro.c
index 58aca00..36ad3ba 100644 (file)
--- a/mro.c
+++ b/mro.c
@@ -1,6 +1,7 @@
 /*    mro.c
  *
  *    Copyright (c) 2007 Brandon L Black
+ *    Copyright (c) 2007, 2008 Larry Wall and others
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -8,8 +9,10 @@
  */
 
 /*
- * "Which order shall we go in?" said Frodo. "Eldest first, or quickest first?
- *  You'll be last either way, Master Peregrin."
+ * 'Which order shall we go in?' said Frodo.  'Eldest first, or quickest first?
+ *  You'll be last either way, Master Peregrin.'
+ *
+ *     [p.101 of _The Lord of the Rings_, I/iii: "A Conspiracy Unmasked"]
  */
 
 /*
@@ -24,18 +27,44 @@ These functions are related to the method resolution order of perl classes
 #define PERL_IN_MRO_C
 #include "perl.h"
 
+struct mro_alg {
+    const char *name;
+    AV *(*resolve)(pTHX_ HV* stash, U32 level);
+};
+
+/* First one is the default */
+static struct mro_alg mros[] = {
+    {"dfs", S_mro_get_linear_isa_dfs},
+    {"c3", S_mro_get_linear_isa_c3}
+};
+
+#define NUMBER_OF_MROS (sizeof(mros)/sizeof(struct mro_alg))
+
+static const struct mro_alg *
+S_get_mro_from_name(pTHX_ const char *const name) {
+    const struct mro_alg *algo = mros;
+    const struct mro_alg *const end = mros + NUMBER_OF_MROS;
+    while (algo < end) {
+       if(strEQ(name, algo->name))
+           return algo;
+       ++algo;
+    }
+    return NULL;
+}
+
 struct mro_meta*
 Perl_mro_meta_init(pTHX_ HV* stash)
 {
     struct mro_meta* newmeta;
 
-    assert(stash);
+    PERL_ARGS_ASSERT_MRO_META_INIT;
     assert(HvAUX(stash));
     assert(!(HvAUX(stash)->xhv_mro_meta));
     Newxz(newmeta, 1, struct mro_meta);
     HvAUX(stash)->xhv_mro_meta = newmeta;
     newmeta->cache_gen = 1;
     newmeta->pkg_gen = 1;
+    newmeta->mro_which = mros;
 
     return newmeta;
 }
@@ -48,26 +77,64 @@ Perl_mro_meta_dup(pTHX_ struct mro_meta* smeta, CLONE_PARAMS* param)
 {
     struct mro_meta* newmeta;
 
-    assert(smeta);
+    PERL_ARGS_ASSERT_MRO_META_DUP;
 
     Newx(newmeta, 1, struct mro_meta);
     Copy(smeta, newmeta, 1, struct mro_meta);
 
     if (newmeta->mro_linear_dfs)
        newmeta->mro_linear_dfs
-           = (AV*) SvREFCNT_inc(sv_dup((SV*)newmeta->mro_linear_dfs, param));
+           = MUTABLE_AV(SvREFCNT_inc(sv_dup((const SV *)newmeta->mro_linear_dfs, param)));
     if (newmeta->mro_linear_c3)
        newmeta->mro_linear_c3
-           = (AV*) SvREFCNT_inc(sv_dup((SV*)newmeta->mro_linear_c3, param));
+           = MUTABLE_AV(SvREFCNT_inc(sv_dup((const SV *)newmeta->mro_linear_c3, param)));
     if (newmeta->mro_nextmethod)
        newmeta->mro_nextmethod
-           = (HV*) SvREFCNT_inc(sv_dup((SV*)newmeta->mro_nextmethod, param));
+           = MUTABLE_HV(SvREFCNT_inc(sv_dup((const SV *)newmeta->mro_nextmethod, param)));
+    if (newmeta->isa)
+       newmeta->isa
+           = MUTABLE_HV(SvREFCNT_inc(sv_dup((const SV *)newmeta->isa, param)));
 
     return newmeta;
 }
 
 #endif /* USE_ITHREADS */
 
+HV *
+Perl_get_isa_hash(pTHX_ HV *const stash)
+{
+    dVAR;
+    struct mro_meta *const meta = HvMROMETA(stash);
+
+    PERL_ARGS_ASSERT_GET_ISA_HASH;
+
+    if (!meta->isa) {
+       AV *const isa = mro_get_linear_isa(stash);
+       if (!meta->isa) {
+           HV *const isa_hash = newHV();
+           /* Linearisation didn't build it for us, so do it here.  */
+           SV *const *svp = AvARRAY(isa);
+           SV *const *const svp_end = svp + AvFILLp(isa) + 1;
+           const HEK *const canon_name = HvNAME_HEK(stash);
+
+           while (svp < svp_end) {
+               (void) hv_store_ent(isa_hash, *svp++, &PL_sv_undef, 0);
+           }
+
+           (void) hv_common(isa_hash, NULL, HEK_KEY(canon_name),
+                            HEK_LEN(canon_name), HEK_FLAGS(canon_name),
+                            HV_FETCH_ISSTORE, &PL_sv_undef,
+                            HEK_HASH(canon_name));
+           (void) hv_store(isa_hash, "UNIVERSAL", 9, &PL_sv_undef, 0);
+
+           SvREADONLY_on(isa_hash);
+
+           meta->isa = isa_hash;
+       }
+    }
+    return meta->isa;
+}
+
 /*
 =for apidoc mro_get_linear_isa_dfs
 
@@ -85,25 +152,27 @@ invalidated).
 =cut
 */
 static AV*
-S_mro_get_linear_isa_dfs(pTHX_ HV *stash, I32 level)
+S_mro_get_linear_isa_dfs(pTHX_ HV *stash, U32 level)
 {
     AV* retval;
     GV** gvp;
     GV* gv;
     AV* av;
-    const char* stashname;
+    const HEK* stashhek;
     struct mro_meta* meta;
+    SV *our_name;
+    HV *stored;
 
-    assert(stash);
+    PERL_ARGS_ASSERT_MRO_GET_LINEAR_ISA_DFS;
     assert(HvAUX(stash));
 
-    stashname = HvNAME_get(stash);
-    if (!stashname)
+    stashhek = HvNAME_HEK(stash);
+    if (!stashhek)
       Perl_croak(aTHX_ "Can't linearize anonymous symbol table");
 
     if (level > 100)
         Perl_croak(aTHX_ "Recursive inheritance detected in package '%s'",
-              stashname);
+                  HEK_KEY(stashhek));
 
     meta = HvMROMETA(stash);
 
@@ -114,21 +183,26 @@ S_mro_get_linear_isa_dfs(pTHX_ HV *stash, I32 level)
 
     /* not in cache, make a new one */
 
-    retval = (AV*)sv_2mortal((SV *)newAV());
-    av_push(retval, newSVpv(stashname, 0)); /* add ourselves at the top */
+    retval = MUTABLE_AV(sv_2mortal(MUTABLE_SV(newAV())));
+    /* We use this later in this function, but don't need a reference to it
+       beyond the end of this function, so reference count is fine.  */
+    our_name = newSVhek(stashhek);
+    av_push(retval, our_name); /* add ourselves at the top */
 
     /* fetch our @ISA */
     gvp = (GV**)hv_fetchs(stash, "ISA", FALSE);
     av = (gvp && (gv = *gvp) && isGV_with_GP(gv)) ? GvAV(gv) : NULL;
 
-    if(av && AvFILLp(av) >= 0) {
+    /* "stored" is used to keep track of all of the classnames we have added to
+       the MRO so far, so we can do a quick exists check and avoid adding
+       duplicate classnames to the MRO as we go.
+       It's then retained to be re-used as a fast lookup for ->isa(), by adding
+       our own name and "UNIVERSAL" to it.  */
 
-        /* "stored" is used to keep track of all of the classnames
-           we have added to the MRO so far, so we can do a quick
-           exists check and avoid adding duplicate classnames to
-           the MRO as we go. */
+    stored = MUTABLE_HV(sv_2mortal(MUTABLE_SV(newHV())));
+
+    if(av && AvFILLp(av) >= 0) {
 
-        HV* const stored = (HV*)sv_2mortal((SV*)newHV());
         SV **svp = AvARRAY(av);
         I32 items = AvFILLp(av) + 1;
 
@@ -159,14 +233,46 @@ S_mro_get_linear_isa_dfs(pTHX_ HV *stash, I32 level)
            }
            while(subrv_items--) {
                SV *const subsv = *subrv_p++;
-               if(!hv_exists_ent(stored, subsv, 0)) {
-                   hv_store_ent(stored, subsv, &PL_sv_undef, 0);
-                   av_push(retval, newSVsv(subsv));
+               /* LVALUE fetch will create a new undefined SV if necessary
+                */
+               HE *const he = hv_fetch_ent(stored, subsv, 1, 0);
+               assert(he);
+               if(HeVAL(he) != &PL_sv_undef) {
+                   /* It was newly created.  Steal it for our new SV, and
+                      replace it in the hash with the "real" thing.  */
+                   SV *const val = HeVAL(he);
+                   HEK *const key = HeKEY_hek(he);
+
+                   HeVAL(he) = &PL_sv_undef;
+                   /* Save copying by making a shared hash key scalar. We
+                      inline this here rather than calling Perl_newSVpvn_share
+                      because we already have the scalar, and we already have
+                      the hash key.  */
+                   assert(SvTYPE(val) == SVt_NULL);
+                   sv_upgrade(val, SVt_PV);
+                   SvPV_set(val, HEK_KEY(share_hek_hek(key)));
+                   SvCUR_set(val, HEK_LEN(key));
+                   SvREADONLY_on(val);
+                   SvFAKE_on(val);
+                   SvPOK_on(val);
+                   if (HEK_UTF8(key))
+                       SvUTF8_on(val);
+
+                   av_push(retval, val);
                }
             }
         }
     }
 
+    (void) hv_store_ent(stored, our_name, &PL_sv_undef, 0);
+    (void) hv_store(stored, "UNIVERSAL", 9, &PL_sv_undef, 0);
+
+    SvREFCNT_inc_simple_void_NN(stored);
+    SvTEMP_off(stored);
+    SvREADONLY_on(stored);
+
+    meta->isa = stored;
+
     /* now that we're past the exception dangers, grab our own reference to
        the AV we're about to use for the result. The reference owned by the
        mortals' stack will be released soon, so everything will balance.  */
@@ -199,27 +305,25 @@ invalidated).
 */
 
 static AV*
-S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
+S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
 {
     AV* retval;
     GV** gvp;
     GV* gv;
     AV* isa;
-    const char* stashname;
-    STRLEN stashname_len;
+    const HEK* stashhek;
     struct mro_meta* meta;
 
-    assert(stash);
+    PERL_ARGS_ASSERT_MRO_GET_LINEAR_ISA_C3;
     assert(HvAUX(stash));
 
-    stashname = HvNAME_get(stash);
-    stashname_len = HvNAMELEN_get(stash);
-    if (!stashname)
+    stashhek = HvNAME_HEK(stash);
+    if (!stashhek)
       Perl_croak(aTHX_ "Can't linearize anonymous symbol table");
 
     if (level > 100)
         Perl_croak(aTHX_ "Recursive inheritance detected in package '%s'",
-              stashname);
+                  HEK_KEY(stashhek));
 
     meta = HvMROMETA(stash);
 
@@ -242,8 +346,8 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
     if(isa && AvFILLp(isa) >= 0) {
         SV** seqs_ptr;
         I32 seqs_items;
-        HV* const tails = (HV*)sv_2mortal((SV*)newHV());
-        AV* const seqs = (AV*)sv_2mortal((SV*)newAV());
+        HV* const tails = MUTABLE_HV(sv_2mortal(MUTABLE_SV(newHV())));
+        AV *const seqs = MUTABLE_AV(sv_2mortal(MUTABLE_SV(newAV())));
         I32* heads;
 
         /* This builds @seqs, which is an array of arrays.
@@ -260,15 +364,15 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
                    containing just itself */
                 AV* const isa_lin = newAV();
                 av_push(isa_lin, newSVsv(isa_item));
-                av_push(seqs, (SV*)isa_lin);
+                av_push(seqs, MUTABLE_SV(isa_lin));
             }
             else {
                 /* recursion */
                 AV* const isa_lin = mro_get_linear_isa_c3(isa_item_stash, level + 1);
-                av_push(seqs, SvREFCNT_inc_simple_NN((SV*)isa_lin));
+                av_push(seqs, SvREFCNT_inc_simple_NN(MUTABLE_SV(isa_lin)));
             }
         }
-        av_push(seqs, SvREFCNT_inc_simple_NN((SV*)isa));
+        av_push(seqs, SvREFCNT_inc_simple_NN(MUTABLE_SV(isa)));
 
         /* This builds "heads", which as an array of integer array
            indices, one per seq, which point at the virtual "head"
@@ -283,18 +387,19 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
         seqs_ptr = AvARRAY(seqs);
         seqs_items = AvFILLp(seqs) + 1;
         while(seqs_items--) {
-            AV* const seq = (AV*)*seqs_ptr++;
+            AV *const seq = MUTABLE_AV(*seqs_ptr++);
             I32 seq_items = AvFILLp(seq);
             if(seq_items > 0) {
                 SV** seq_ptr = AvARRAY(seq) + 1;
                 while(seq_items--) {
                     SV* const seqitem = *seq_ptr++;
-                    HE* const he = hv_fetch_ent(tails, seqitem, 0, 0);
-                    if(!he) {
-                        hv_store_ent(tails, seqitem, newSViv(1), 0);
-                    }
-                    else {
+                   /* LVALUE fetch will create a new undefined SV if necessary
+                    */
+                    HE* const he = hv_fetch_ent(tails, seqitem, 1, 0);
+                    if(he) {
                         SV* const val = HeVAL(he);
+                       /* This will increment undef to 1, which is what we
+                          want for a newly created entry.  */
                         sv_inc(val);
                     }
                 }
@@ -303,7 +408,7 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
 
         /* Initialize retval to build the return value in */
         retval = newAV();
-        av_push(retval, newSVpvn(stashname, stashname_len)); /* us first */
+        av_push(retval, newSVhek(stashhek)); /* us first */
 
         /* This loop won't terminate until we either finish building
            the MRO, or get an exception. */
@@ -316,7 +421,7 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
             SV** const avptr = AvARRAY(seqs);
             for(s = 0; s <= AvFILLp(seqs); s++) {
                 SV** svp;
-                AV * const seq = (AV*)(avptr[s]);
+                AV * const seq = MUTABLE_AV(avptr[s]);
                SV* seqhead;
                 if(!seq) continue; /* skip empty seqs */
                 svp = av_fetch(seq, heads[s], 0);
@@ -382,14 +487,14 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, I32 level)
                 Safefree(heads);
 
                 Perl_croak(aTHX_ "Inconsistent hierarchy during C3 merge of class '%s': "
-                    "merging failed on parent '%"SVf"'", stashname, SVfARG(cand));
+                    "merging failed on parent '%"SVf"'", HEK_KEY(stashhek), SVfARG(cand));
             }
         }
     }
     else { /* @ISA was undefined or empty */
         /* build a retval containing only ourselves */
         retval = newAV();
-        av_push(retval, newSVpvn(stashname, stashname_len));
+        av_push(retval, newSVhek(stashhek));
     }
 
     /* we don't want anyone modifying the cache entry but us,
@@ -422,19 +527,14 @@ Perl_mro_get_linear_isa(pTHX_ HV *stash)
 {
     struct mro_meta* meta;
 
-    assert(stash);
+    PERL_ARGS_ASSERT_MRO_GET_LINEAR_ISA;
     if(!SvOOK(stash))
         Perl_croak(aTHX_ "Can't linearize anonymous symbol table");
 
     meta = HvMROMETA(stash);
-    if(meta->mro_which == MRO_DFS) {
-        return mro_get_linear_isa_dfs(stash, 0);
-    } else if(meta->mro_which == MRO_C3) {
-        return mro_get_linear_isa_c3(stash, 0);
-    } else {
+    if (!meta->mro_which)
         Perl_croak(aTHX_ "panic: invalid MRO!");
-    }
-    return NULL; /* NOT REACHED */
+    return meta->mro_which->resolve(aTHX_ stash, 0);
 }
 
 /*
@@ -461,15 +561,21 @@ Perl_mro_isa_changed_in(pTHX_ HV* stash)
     const char * const stashname = HvNAME_get(stash);
     const STRLEN stashname_len = HvNAMELEN_get(stash);
 
+    PERL_ARGS_ASSERT_MRO_ISA_CHANGED_IN;
+
     if(!stashname)
         Perl_croak(aTHX_ "Can't call mro_isa_changed_in() on anonymous symbol table");
 
     /* wipe out the cached linearizations for this stash */
     meta = HvMROMETA(stash);
-    SvREFCNT_dec((SV*)meta->mro_linear_dfs);
-    SvREFCNT_dec((SV*)meta->mro_linear_c3);
+    SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_dfs));
+    SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_c3));
     meta->mro_linear_dfs = NULL;
     meta->mro_linear_c3 = NULL;
+    if (meta->isa) {
+       SvREFCNT_dec(meta->isa);
+       meta->isa = NULL;
+    }
 
     /* Inc the package generation, since our @ISA changed */
     meta->pkg_gen++;
@@ -478,7 +584,7 @@ Perl_mro_isa_changed_in(pTHX_ HV* stash)
        is UNIVERSAL or one of its parents */
 
     svp = hv_fetch(PL_isarev, stashname, stashname_len, 0);
-    isarev = svp ? (HV*)*svp : NULL;
+    isarev = svp ? MUTABLE_HV(*svp) : NULL;
 
     if((stashname_len == 9 && strEQ(stashname, "UNIVERSAL"))
         || (isarev && hv_exists(isarev, "UNIVERSAL", 9))) {
@@ -498,14 +604,15 @@ Perl_mro_isa_changed_in(pTHX_ HV* stash)
     if(isarev) {
         hv_iterinit(isarev);
         while((iter = hv_iternext(isarev))) {
-            SV* const revkey = hv_iterkeysv(iter);
-            HV* revstash = gv_stashsv(revkey, 0);
+           I32 len;
+            const char* const revkey = hv_iterkey(iter, &len);
+            HV* revstash = gv_stashpvn(revkey, len, 0);
             struct mro_meta* revmeta;
 
             if(!revstash) continue;
             revmeta = HvMROMETA(revstash);
-            SvREFCNT_dec((SV*)revmeta->mro_linear_dfs);
-            SvREFCNT_dec((SV*)revmeta->mro_linear_c3);
+            SvREFCNT_dec(MUTABLE_SV(revmeta->mro_linear_dfs));
+            SvREFCNT_dec(MUTABLE_SV(revmeta->mro_linear_c3));
             revmeta->mro_linear_dfs = NULL;
             revmeta->mro_linear_c3 = NULL;
             if(!is_universal)
@@ -530,25 +637,29 @@ Perl_mro_isa_changed_in(pTHX_ HV* stash)
         SV* const sv = *svp++;
         HV* mroisarev;
 
-        HE *he = hv_fetch_ent(PL_isarev, sv, 0, 0);
-        if(!he) {
-            he = hv_store_ent(PL_isarev, sv, (SV*)newHV(), 0);
-        }
-        mroisarev = (HV*)HeVAL(he);
+        HE *he = hv_fetch_ent(PL_isarev, sv, TRUE, 0);
+
+       /* That fetch should not fail.  But if it had to create a new SV for
+          us, then will need to upgrade it to an HV (which sv_upgrade() can
+          now do for us. */
+
+        mroisarev = MUTABLE_HV(HeVAL(he));
+
+       SvUPGRADE(MUTABLE_SV(mroisarev), SVt_PVHV);
 
        /* This hash only ever contains PL_sv_yes. Storing it over itself is
           almost as cheap as calling hv_exists, so on aggregate we expect to
           save time by not making two calls to the common HV code for the
           case where it doesn't exist.  */
           
-       hv_store(mroisarev, stashname, stashname_len, &PL_sv_yes, 0);
+       (void)hv_store(mroisarev, stashname, stashname_len, &PL_sv_yes, 0);
 
         if(isarev) {
             hv_iterinit(isarev);
             while((iter = hv_iternext(isarev))) {
                 I32 revkeylen;
                 char* const revkey = hv_iterkey(iter, &revkeylen);
-               hv_store(mroisarev, revkey, revkeylen, &PL_sv_yes, 0);
+               (void)hv_store(mroisarev, revkey, revkeylen, &PL_sv_yes, 0);
             }
         }
     }
@@ -591,7 +702,9 @@ Perl_mro_method_changed_in(pTHX_ HV *stash)
     const STRLEN stashname_len = HvNAMELEN_get(stash);
 
     SV ** const svp = hv_fetch(PL_isarev, stashname, stashname_len, 0);
-    HV * const isarev = svp ? (HV*)*svp : NULL;
+    HV * const isarev = svp ? MUTABLE_HV(*svp) : NULL;
+
+    PERL_ARGS_ASSERT_MRO_METHOD_CHANGED_IN;
 
     if(!stashname)
         Perl_croak(aTHX_ "Can't call mro_method_changed_in() on anonymous symbol table");
@@ -614,8 +727,9 @@ Perl_mro_method_changed_in(pTHX_ HV *stash)
 
         hv_iterinit(isarev);
         while((iter = hv_iternext(isarev))) {
-            SV* const revkey = hv_iterkeysv(iter);
-            HV* const revstash = gv_stashsv(revkey, 0);
+           I32 len;
+            const char* const revkey = hv_iterkey(iter, &len);
+            HV* const revstash = gv_stashpvn(revkey, len, 0);
             struct mro_meta* mrometa;
 
             if(!revstash) continue;
@@ -677,10 +791,8 @@ XS(XS_mro_get_linear_isa) {
     HV* class_stash;
     SV* classname;
 
-    PERL_UNUSED_ARG(cv);
-
     if(items < 1 || items > 2)
-       Perl_croak(aTHX_ "Usage: mro::get_linear_isa(classname [, type ])");
+       croak_xs_usage(cv, "classname [, type ]");
 
     classname = ST(0);
     class_stash = gv_stashsv(classname, 0);
@@ -689,23 +801,21 @@ XS(XS_mro_get_linear_isa) {
         /* No stash exists yet, give them just the classname */
         AV* isalin = newAV();
         av_push(isalin, newSVsv(classname));
-        ST(0) = sv_2mortal(newRV_noinc((SV*)isalin));
+        ST(0) = sv_2mortal(newRV_noinc(MUTABLE_SV(isalin)));
         XSRETURN(1);
     }
     else if(items > 1) {
         const char* const which = SvPV_nolen(ST(1));
-        if(strEQ(which, "dfs"))
-            RETVAL = mro_get_linear_isa_dfs(class_stash, 0);
-        else if(strEQ(which, "c3"))
-            RETVAL = mro_get_linear_isa_c3(class_stash, 0);
-        else
-            Perl_croak(aTHX_ "Invalid mro name: '%s'", which);
+       const struct mro_alg *const algo = S_get_mro_from_name(aTHX_ which);
+       if (!algo)
+           Perl_croak(aTHX_ "Invalid mro name: '%s'", which);
+       RETVAL = algo->resolve(aTHX_ class_stash, 0);
     }
     else {
         RETVAL = mro_get_linear_isa(class_stash);
     }
 
-    ST(0) = newRV_inc((SV*)RETVAL);
+    ST(0) = newRV_inc(MUTABLE_SV(RETVAL));
     sv_2mortal(ST(0));
     XSRETURN(1);
 }
@@ -715,15 +825,13 @@ XS(XS_mro_set_mro)
     dVAR;
     dXSARGS;
     SV* classname;
-    char* whichstr;
-    mro_alg which;
+    const char* whichstr;
+    const struct mro_alg *which;
     HV* class_stash;
     struct mro_meta* meta;
 
-    PERL_UNUSED_ARG(cv);
-
     if (items != 2)
-       Perl_croak(aTHX_ "Usage: mro::set_mro(classname, type)");
+       croak_xs_usage(cv, "classname, type");
 
     classname = ST(0);
     whichstr = SvPV_nolen(ST(1));
@@ -731,11 +839,8 @@ XS(XS_mro_set_mro)
     if(!class_stash) Perl_croak(aTHX_ "Cannot create class: '%"SVf"'!", SVfARG(classname));
     meta = HvMROMETA(class_stash);
 
-    if(strEQ(whichstr, "dfs"))
-        which = MRO_DFS;
-    else if(strEQ(whichstr, "c3"))
-        which = MRO_C3;
-    else
+    which = S_get_mro_from_name(aTHX_ whichstr);
+    if (!which)
         Perl_croak(aTHX_ "Invalid mro name: '%s'", whichstr);
 
     if(meta->mro_which != which) {
@@ -758,19 +863,15 @@ XS(XS_mro_get_mro)
     SV* classname;
     HV* class_stash;
 
-    PERL_UNUSED_ARG(cv);
-
     if (items != 1)
-       Perl_croak(aTHX_ "Usage: mro::get_mro(classname)");
+       croak_xs_usage(cv, "classname");
 
     classname = ST(0);
     class_stash = gv_stashsv(classname, 0);
 
-    if(!class_stash || HvMROMETA(class_stash)->mro_which == MRO_DFS)
-        ST(0) = sv_2mortal(newSVpvn("dfs", 3));
-    else
-        ST(0) = sv_2mortal(newSVpvn("c3", 2));
-
+    ST(0) = sv_2mortal(newSVpv(class_stash
+                              ? HvMROMETA(class_stash)->mro_which->name
+                              : "dfs", 0));
     XSRETURN(1);
 }
 
@@ -783,10 +884,8 @@ XS(XS_mro_get_isarev)
     HV* isarev;
     AV* ret_array;
 
-    PERL_UNUSED_ARG(cv);
-
     if (items != 1)
-       Perl_croak(aTHX_ "Usage: mro::get_isarev(classname)");
+       croak_xs_usage(cv, "classname");
 
     classname = ST(0);
 
@@ -794,7 +893,7 @@ XS(XS_mro_get_isarev)
 
     
     he = hv_fetch_ent(PL_isarev, classname, 0, 0);
-    isarev = he ? (HV*)HeVAL(he) : NULL;
+    isarev = he ? MUTABLE_HV(HeVAL(he)) : NULL;
 
     ret_array = newAV();
     if(isarev) {
@@ -803,7 +902,7 @@ XS(XS_mro_get_isarev)
         while((iter = hv_iternext(isarev)))
             av_push(ret_array, newSVsv(hv_iterkeysv(iter)));
     }
-    XPUSHs(sv_2mortal(newRV_noinc((SV*)ret_array)));
+    mXPUSHs(newRV_noinc(MUTABLE_SV(ret_array)));
 
     PUTBACK;
     return;
@@ -819,18 +918,15 @@ XS(XS_mro_is_universal)
     STRLEN classname_len;
     HE* he;
 
-    PERL_UNUSED_ARG(cv);
-
     if (items != 1)
-       Perl_croak(aTHX_ "Usage: mro::is_universal(classname)");
+       croak_xs_usage(cv, "classname");
 
     classname = ST(0);
 
-    classname_pv = SvPV_nolen(classname);
-    classname_len = strlen(classname_pv);
+    classname_pv = SvPV(classname,classname_len);
 
     he = hv_fetch_ent(PL_isarev, classname, 0, 0);
-    isarev = he ? (HV*)HeVAL(he) : NULL;
+    isarev = he ? MUTABLE_HV(HeVAL(he)) : NULL;
 
     if((classname_len == 9 && strEQ(classname_pv, "UNIVERSAL"))
         || (isarev && hv_exists(isarev, "UNIVERSAL", 9)))
@@ -844,10 +940,8 @@ XS(XS_mro_invalidate_method_caches)
     dVAR;
     dXSARGS;
 
-    PERL_UNUSED_ARG(cv);
-
     if (items != 0)
-        Perl_croak(aTHX_ "Usage: mro::invalidate_all_method_caches()");
+       croak_xs_usage(cv, "");
 
     PL_sub_generation++;
 
@@ -861,10 +955,8 @@ XS(XS_mro_method_changed_in)
     SV* classname;
     HV* class_stash;
 
-    PERL_UNUSED_ARG(cv);
-
     if(items != 1)
-        Perl_croak(aTHX_ "Usage: mro::method_changed_in(classname)");
+       croak_xs_usage(cv, "classname");
     
     classname = ST(0);
 
@@ -883,10 +975,8 @@ XS(XS_mro_get_pkg_gen)
     SV* classname;
     HV* class_stash;
 
-    PERL_UNUSED_ARG(cv);
-
     if(items != 1)
-        Perl_croak(aTHX_ "Usage: mro::get_pkg_gen(classname)");
+       croak_xs_usage(cv, "classname");
     
     classname = ST(0);
 
@@ -894,9 +984,7 @@ XS(XS_mro_get_pkg_gen)
 
     SP -= items;
 
-    XPUSHs(sv_2mortal(newSViv(
-        class_stash ? HvMROMETA(class_stash)->pkg_gen : 0
-    )));
+    mXPUSHi(class_stash ? HvMROMETA(class_stash)->pkg_gen : 0);
     
     PUTBACK;
     return;
@@ -934,7 +1022,7 @@ XS(XS_mro_nextcan)
     if(sv_isobject(self))
         selfstash = SvSTASH(SvRV(self));
     else
-        selfstash = gv_stashsv(self, 0);
+        selfstash = gv_stashsv(self, GV_ADD);
 
     assert(selfstash);
 
@@ -1023,7 +1111,7 @@ XS(XS_mro_nextcan)
                    Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
                 XSRETURN_EMPTY;
            }
-           XPUSHs(sv_2mortal(newRV_inc(val)));
+           mXPUSHs(newRV_inc(val));
             XSRETURN(1);
        }
     }
@@ -1031,7 +1119,7 @@ XS(XS_mro_nextcan)
     /* 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));
+    stashname = newSVpvn_flags(fq_subname, stashname_len, SVs_TEMP);
 
     linear_av = mro_get_linear_isa_c3(selfstash, 0); /* has ourselves at the top of the list */
 
@@ -1082,15 +1170,15 @@ XS(XS_mro_nextcan)
                entries, because in C3 the method cache of a parent is not
                valid for the child */
             if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) {
-                SvREFCNT_inc_simple_void_NN((SV*)cand_cv);
-                hv_store_ent(nmcache, newSVsv(sv), (SV*)cand_cv, 0);
-                XPUSHs(sv_2mortal(newRV_inc((SV*)cand_cv)));
+                SvREFCNT_inc_simple_void_NN(MUTABLE_SV(cand_cv));
+                (void)hv_store_ent(nmcache, newSVsv(sv), MUTABLE_SV(cand_cv), 0);
+                mXPUSHs(newRV_inc(MUTABLE_SV(cand_cv)));
                 XSRETURN(1);
             }
         }
     }
 
-    hv_store_ent(nmcache, newSVsv(sv), &PL_sv_undef, 0);
+    (void)hv_store_ent(nmcache, newSVsv(sv), &PL_sv_undef, 0);
     if(throw_nomethod)
         Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
     XSRETURN_EMPTY;