This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add MUTABLE_CV(), and eliminate (CV *) casts in *.c.
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index a1f51aa..38ba12e 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -1,7 +1,7 @@
 /*    pp.c
  *
- *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- *    2000, 2001, 2002, 2003, 2004, 2005, 2006, by Larry Wall and others
+ *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ *    2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by 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.
@@ -61,7 +61,8 @@ PP(pp_padav)
     dVAR; dSP; dTARGET;
     I32 gimme;
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
+       if (!(PL_op->op_private & OPpPAD_STATE))
+           SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     EXTEND(SP, 1);
     if (PL_op->op_flags & OPf_REF) {
        PUSHs(TARG);
@@ -104,7 +105,8 @@ PP(pp_padhv)
 
     XPUSHs(TARG);
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
+       if (!(PL_op->op_private & OPpPAD_STATE))
+           SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     if (PL_op->op_flags & OPf_REF)
        RETURN;
     else if (LVRET) {
@@ -117,7 +119,7 @@ PP(pp_padhv)
        RETURNOP(do_kv());
     }
     else if (gimme == G_SCALAR) {
-       SV* const sv = Perl_hv_scalar(aTHX_ (HV*)TARG);
+       SV* const sv = Perl_hv_scalar(aTHX_ MUTABLE_HV(TARG));
        SETs(sv);
     }
     RETURN;
@@ -141,11 +143,11 @@ PP(pp_rv2gv)
            SvREFCNT_inc_void_NN(sv);
            sv = (SV*) gv;
        }
-       else if (SvTYPE(sv) != SVt_PVGV)
+       else if (!isGV_with_GP(sv))
            DIE(aTHX_ "Not a GLOB reference");
     }
     else {
-       if (SvTYPE(sv) != SVt_PVGV) {
+       if (!isGV_with_GP(sv)) {
            if (SvGMAGICAL(sv)) {
                mg_get(sv);
                if (SvROK(sv))
@@ -170,13 +172,7 @@ PP(pp_rv2gv)
                        const char * const name = CopSTASHPV(PL_curcop);
                        gv = newGVgen(name);
                    }
-                   if (SvTYPE(sv) < SVt_RV)
-                       sv_upgrade(sv, SVt_RV);
-                   else if (SvPVX_const(sv)) {
-                       SvPV_free(sv);
-                       SvLEN_set(sv, 0);
-                        SvCUR_set(sv, 0);
-                   }
+                   prepare_SV_for_RV(sv);
                    SvRV_set(sv, (SV*)gv);
                    SvROK_on(sv);
                    SvSETMAGIC(sv);
@@ -220,6 +216,52 @@ PP(pp_rv2gv)
     RETURN;
 }
 
+/* Helper function for pp_rv2sv and pp_rv2av  */
+GV *
+Perl_softref2xv(pTHX_ SV *const sv, const char *const what,
+               const svtype type, SV ***spp)
+{
+    dVAR;
+    GV *gv;
+
+    PERL_ARGS_ASSERT_SOFTREF2XV;
+
+    if (PL_op->op_private & HINT_STRICT_REFS) {
+       if (SvOK(sv))
+           Perl_die(aTHX_ PL_no_symref_sv, sv, what);
+       else
+           Perl_die(aTHX_ PL_no_usym, what);
+    }
+    if (!SvOK(sv)) {
+       if (PL_op->op_flags & OPf_REF)
+           Perl_die(aTHX_ PL_no_usym, what);
+       if (ckWARN(WARN_UNINITIALIZED))
+           report_uninit(sv);
+       if (type != SVt_PV && GIMME_V == G_ARRAY) {
+           (*spp)--;
+           return NULL;
+       }
+       **spp = &PL_sv_undef;
+       return NULL;
+    }
+    if ((PL_op->op_flags & OPf_SPECIAL) &&
+       !(PL_op->op_flags & OPf_MOD))
+       {
+           gv = gv_fetchsv(sv, 0, type);
+           if (!gv
+               && (!is_gv_magical_sv(sv,0)
+                   || !(gv = gv_fetchsv(sv, GV_ADD, type))))
+               {
+                   **spp = &PL_sv_undef;
+                   return NULL;
+               }
+       }
+    else {
+       gv = gv_fetchsv(sv, GV_ADD, type);
+    }
+    return gv;
+}
+
 PP(pp_rv2sv)
 {
     dVAR; dSP; dTOPss;
@@ -237,44 +279,21 @@ PP(pp_rv2sv)
        case SVt_PVFM:
        case SVt_PVIO:
            DIE(aTHX_ "Not a SCALAR reference");
+       default: NOOP;
        }
     }
     else {
        gv = (GV*)sv;
 
-       if (SvTYPE(gv) != SVt_PVGV) {
+       if (!isGV_with_GP(gv)) {
            if (SvGMAGICAL(sv)) {
                mg_get(sv);
                if (SvROK(sv))
                    goto wasref;
            }
-           if (PL_op->op_private & HINT_STRICT_REFS) {
-               if (SvOK(sv))
-                   DIE(aTHX_ PL_no_symref_sv, sv, "a SCALAR");
-               else
-                   DIE(aTHX_ PL_no_usym, "a SCALAR");
-           }
-           if (!SvOK(sv)) {
-               if (PL_op->op_flags & OPf_REF)
-                   DIE(aTHX_ PL_no_usym, "a SCALAR");
-               if (ckWARN(WARN_UNINITIALIZED))
-                   report_uninit(sv);
-               RETSETUNDEF;
-           }
-           if ((PL_op->op_flags & OPf_SPECIAL) &&
-               !(PL_op->op_flags & OPf_MOD))
-           {
-               gv = (GV*)gv_fetchsv(sv, 0, SVt_PV);
-               if (!gv
-                   && (!is_gv_magical_sv(sv, 0)
-                       || !(gv = (GV*)gv_fetchsv(sv, GV_ADD, SVt_PV))))
-               {
-                   RETSETUNDEF;
-               }
-           }
-           else {
-               gv = (GV*)gv_fetchsv(sv, GV_ADD, SVt_PV);
-           }
+           gv = Perl_softref2xv(aTHX_ sv, "a SCALAR", SVt_PV, &sp);
+           if (!gv)
+               RETURN;
        }
        sv = GvSVn(gv);
     }
@@ -300,8 +319,7 @@ PP(pp_av2arylen)
     AV * const av = (AV*)TOPs;
     SV ** const sv = Perl_av_arylen_p(aTHX_ (AV*)av);
     if (!*sv) {
-       *sv = newSV(0);
-       sv_upgrade(*sv, SVt_PVMG);
+       *sv = newSV_type(SVt_PVMG);
        sv_magic(*sv, (SV*)av, PERL_MAGIC_arylen, NULL, 0);
     }
     SETs(*sv);
@@ -346,7 +364,7 @@ PP(pp_rv2cv)
 {
     dVAR; dSP;
     GV *gv;
-    HV *stash;
+    HV *stash_unused;
     const I32 flags = (PL_op->op_flags & OPf_SPECIAL)
        ? 0
        : ((PL_op->op_private & (OPpLVAL_INTRO|OPpMAY_RETURN_CONSTANT)) == OPpMAY_RETURN_CONSTANT)
@@ -355,10 +373,10 @@ PP(pp_rv2cv)
     /* We usually try to add a non-existent subroutine in case of AUTOLOAD. */
     /* (But not in defined().) */
 
-    CV *cv = sv_2cv(TOPs, &stash, &gv, flags);
+    CV *cv = sv_2cv(TOPs, &stash_unused, &gv, flags);
     if (cv) {
        if (CvCLONE(cv))
-           cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
+           cv = MUTABLE_CV(sv_2mortal((SV*)cv_clone(cv)));
        if ((PL_op->op_private & OPpLVAL_INTRO)) {
            if (gv && GvCV(gv) == cv && (gv = gv_autoload4(GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv), FALSE)))
                cv = GvCV(gv);
@@ -367,10 +385,10 @@ PP(pp_rv2cv)
        }
     }
     else if ((flags == (GV_ADD|GV_NOEXPAND)) && gv && SvROK(gv)) {
-       cv = (CV*)gv;
+       cv = MUTABLE_CV(gv);
     }    
     else
-       cv = (CV*)&PL_sv_undef;
+       cv = MUTABLE_CV(&PL_sv_undef);
     SETs((SV*)cv);
     RETURN;
 }
@@ -384,18 +402,25 @@ PP(pp_prototype)
     SV *ret = &PL_sv_undef;
 
     if (SvPOK(TOPs) && SvCUR(TOPs) >= 7) {
-       const char * const s = SvPVX_const(TOPs);
+       const char * s = SvPVX_const(TOPs);
        if (strnEQ(s, "CORE::", 6)) {
-           const int code = keyword(s + 6, SvCUR(TOPs) - 6);
+           const int code = keyword(s + 6, SvCUR(TOPs) - 6, 1);
            if (code < 0) {     /* Overridable. */
 #define MAX_ARGS_OP ((sizeof(I32) - 1) * 2)
-               int i = 0, n = 0, seen_question = 0;
+               int i = 0, n = 0, seen_question = 0, defgv = 0;
                I32 oa;
                char str[ MAX_ARGS_OP * 2 + 2 ]; /* One ';', one '\0' */
 
                if (code == -KEY_chop || code == -KEY_chomp
                        || code == -KEY_exec || code == -KEY_system)
                    goto set;
+               if (code == -KEY_mkdir) {
+                   ret = newSVpvs_flags("_;$", SVs_TEMP);
+                   goto set;
+               }
+               if (code == -KEY_readpipe) {
+                   s = "CORE::backtick";
+               }
                while (i < MAXO) {      /* The slow way. */
                    if (strEQ(s + 6, PL_op_name[i])
                        || strEQ(s + 6, PL_op_desc[i]))
@@ -406,9 +431,10 @@ PP(pp_prototype)
                }
                goto nonesuch;          /* Should not happen... */
              found:
+               defgv = PL_opargs[i] & OA_DEFGV;
                oa = PL_opargs[i] >> OASHIFT;
                while (oa) {
-                   if (oa & OA_OPTIONAL && !seen_question) {
+                   if (oa & OA_OPTIONAL && !seen_question && !defgv) {
                        seen_question = 1;
                        str[n++] = ';';
                    }
@@ -422,8 +448,10 @@ PP(pp_prototype)
                    str[n++] = ("?$@@%&*$")[oa & (OA_OPTIONAL - 1)];
                    oa = oa >> 4;
                }
+               if (defgv && str[n - 1] == '$')
+                   str[n - 1] = '_';
                str[n++] = '\0';
-               ret = sv_2mortal(newSVpvn(str, n - 1));
+               ret = newSVpvn_flags(str, n - 1, SVs_TEMP);
            }
            else if (code)              /* Non-Overridable */
                goto set;
@@ -435,7 +463,7 @@ PP(pp_prototype)
     }
     cv = sv_2cv(TOPs, &stash, &gv, 0);
     if (cv && SvPOK(cv))
-       ret = sv_2mortal(newSVpvn(SvPVX_const(cv), SvCUR(cv)));
+       ret = newSVpvn_flags(SvPVX_const(cv), SvCUR(cv), SVs_TEMP);
   set:
     SETs(ret);
     RETURN;
@@ -444,9 +472,9 @@ PP(pp_prototype)
 PP(pp_anoncode)
 {
     dVAR; dSP;
-    CV* cv = (CV*)PAD_SV(PL_op->op_targ);
+    CV *cv = MUTABLE_CV(PAD_SV(PL_op->op_targ));
     if (CvCLONE(cv))
-       cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
+       cv = MUTABLE_CV(sv_2mortal((SV*)cv_clone(cv)));
     EXTEND(SP,1);
     PUSHs((SV*)cv);
     RETURN;
@@ -483,6 +511,8 @@ S_refto(pTHX_ SV *sv)
     dVAR;
     SV* rv;
 
+    PERL_ARGS_ASSERT_REFTO;
+
     if (SvTYPE(sv) == SVt_PVLV && LvTYPE(sv) == 'y') {
        if (LvTARGLEN(sv))
            vivify_defelem(sv);
@@ -504,7 +534,7 @@ S_refto(pTHX_ SV *sv)
        SvREFCNT_inc_void_NN(sv);
     }
     rv = sv_newmortal();
-    sv_upgrade(rv, SVt_RV);
+    sv_upgrade(rv, SVt_IV);
     SvRV_set(rv, sv);
     SvROK_on(rv);
     return rv;
@@ -545,7 +575,7 @@ PP(pp_bless)
        if (len == 0 && ckWARN(WARN_MISC))
            Perl_warner(aTHX_ packWARN(WARN_MISC),
                   "Explicit blessing to '' (assuming package main)");
-       stash = gv_stashpvn(ptr, len, TRUE);
+       stash = gv_stashpvn(ptr, len, GV_ADD);
     }
 
     (void)sv_bless(TOPs, stash);
@@ -598,7 +628,7 @@ PP(pp_gelem)
            break;
        case 'N':
            if (strEQ(second_letter, "AME"))
-               sv = newSVpvn(GvNAME(gv), GvNAMELEN(gv));
+               sv = newSVhek(GvNAME_HEK(gv));
            break;
        case 'P':
            if (strEQ(second_letter, "ACKAGE")) {
@@ -641,7 +671,7 @@ PP(pp_study)
     }
     s = (unsigned char*)(SvPV(sv, len));
     pos = len;
-    if (pos <= 0 || !SvPOK(sv)) {
+    if (pos <= 0 || !SvPOK(sv) || SvUTF8(sv)) {
        /* No point in studying a zero length string, and not safe to study
           anything that doesn't appear to be a simple scalar (and hence might
           change between now and when the regexp engine runs without our set
@@ -776,26 +806,38 @@ PP(pp_undef)
        av_undef((AV*)sv);
        break;
     case SVt_PVHV:
-       hv_undef((HV*)sv);
+       hv_undef(MUTABLE_HV(sv));
        break;
     case SVt_PVCV:
-       if (cv_const_sv((CV*)sv) && ckWARN(WARN_MISC))
+       if (cv_const_sv((const CV *)sv) && ckWARN(WARN_MISC))
            Perl_warner(aTHX_ packWARN(WARN_MISC), "Constant subroutine %s undefined",
-                CvANON((CV*)sv) ? "(anonymous)" : GvENAME(CvGV((CV*)sv)));
+                CvANON((const CV *)sv) ? "(anonymous)"
+                       : GvENAME(CvGV((const CV *)sv)));
        /* FALLTHROUGH */
     case SVt_PVFM:
        {
            /* let user-undef'd sub keep its identity */
-           GV* const gv = CvGV((CV*)sv);
-           cv_undef((CV*)sv);
-           CvGV((CV*)sv) = gv;
+           GV* const gv = CvGV((const CV *)sv);
+           cv_undef(MUTABLE_CV(sv));
+           CvGV((const CV *)sv) = gv;
        }
        break;
     case SVt_PVGV:
-       if (SvFAKE(sv))
+       if (SvFAKE(sv)) {
            SvSetMagicSV(sv, &PL_sv_undef);
-       else {
+           break;
+       }
+       else if (isGV_with_GP(sv)) {
            GP *gp;
+            HV *stash;
+
+            /* undef *Foo:: */
+            if((stash = GvHV((GV*)sv)) && HvNAME_get(stash))
+                mro_isa_changed_in(stash);
+            /* undef *Pkg::meth_name ... */
+            else if(GvCVu((GV*)sv) && (stash = GvSTASH((GV*)sv)) && HvNAME_get(stash))
+                mro_method_changed_in(stash);
+
            gp_free((GV*)sv);
            Newxz(gp, 1, GP);
            GvGP(sv) = gp_ref(gp);
@@ -803,8 +845,9 @@ PP(pp_undef)
            GvLINE(sv) = CopLINE(PL_curcop);
            GvEGV(sv) = (GV*)sv;
            GvMULTI_on(sv);
+           break;
        }
-       break;
+       /* FALL THROUGH */
     default:
        if (SvTYPE(sv) >= SVt_PV && SvPVX_const(sv) && SvLEN(sv)) {
            SvPV_free(sv);
@@ -821,7 +864,7 @@ PP(pp_undef)
 PP(pp_predec)
 {
     dVAR; dSP;
-    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVAV || isGV_with_GP(TOPs))
        DIE(aTHX_ PL_no_modify);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
         && SvIVX(TOPs) != IV_MIN)
@@ -838,7 +881,7 @@ PP(pp_predec)
 PP(pp_postinc)
 {
     dVAR; dSP; dTARGET;
-    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVAV || isGV_with_GP(TOPs))
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
@@ -860,7 +903,7 @@ PP(pp_postinc)
 PP(pp_postdec)
 {
     dVAR; dSP; dTARGET;
-    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVAV || isGV_with_GP(TOPs))
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
@@ -880,28 +923,30 @@ PP(pp_postdec)
 
 PP(pp_pow)
 {
-    dVAR; dSP; dATARGET;
+    dVAR; dSP; dATARGET; SV *svl, *svr;
 #ifdef PERL_PRESERVE_IVUV
     bool is_int = 0;
 #endif
     tryAMAGICbin(pow,opASSIGN);
+    svl = sv_2num(TOPm1s);
+    svr = sv_2num(TOPs);
 #ifdef PERL_PRESERVE_IVUV
     /* For integer to integer power, we do the calculation by hand wherever
        we're sure it is safe; otherwise we call pow() and try to convert to
        integer afterwards. */
     {
-       SvIV_please(TOPs);
-       if (SvIOK(TOPs)) {
-           SvIV_please(TOPm1s);
-           if (SvIOK(TOPm1s)) {
+       SvIV_please(svr);
+       if (SvIOK(svr)) {
+           SvIV_please(svl);
+           if (SvIOK(svl)) {
                UV power;
                bool baseuok;
                UV baseuv;
 
-               if (SvUOK(TOPs)) {
-                   power = SvUVX(TOPs);
+               if (SvUOK(svr)) {
+                   power = SvUVX(svr);
                } else {
-                   const IV iv = SvIVX(TOPs);
+                   const IV iv = SvIVX(svr);
                    if (iv >= 0) {
                        power = iv;
                    } else {
@@ -909,11 +954,11 @@ PP(pp_pow)
                    }
                }
 
-               baseuok = SvUOK(TOPm1s);
+               baseuok = SvUOK(svl);
                if (baseuok) {
-                   baseuv = SvUVX(TOPm1s);
+                   baseuv = SvUVX(svl);
                } else {
-                   const IV iv = SvIVX(TOPm1s);
+                   const IV iv = SvIVX(svl);
                    if (iv >= 0) {
                        baseuv = iv;
                        baseuok = TRUE; /* effectively it's a UV now */
@@ -948,7 +993,7 @@ PP(pp_pow)
                    }
                     SP--;
                     SETn( result );
-                    SvIV_please(TOPs);
+                    SvIV_please(svr);
                     RETURN;
                } else {
                    register unsigned int highbit = 8 * sizeof(UV);
@@ -997,7 +1042,9 @@ PP(pp_pow)
   float_it:
 #endif    
     {
-       dPOPTOPnnrl;
+       NV right = SvNV(svr);
+       NV left  = SvNV(svl);
+       (void)POPs;
 
 #if defined(USE_LONG_DOUBLE) && defined(HAS_AIX_POWL_NEG_BASE_BUG)
     /*
@@ -1041,7 +1088,7 @@ PP(pp_pow)
 
 #ifdef PERL_PRESERVE_IVUV
        if (is_int)
-           SvIV_please(TOPs);
+           SvIV_please(svr);
 #endif
        RETURN;
     }
@@ -1049,18 +1096,21 @@ PP(pp_pow)
 
 PP(pp_multiply)
 {
-    dVAR; dSP; dATARGET; tryAMAGICbin(mult,opASSIGN);
+    dVAR; dSP; dATARGET; SV *svl, *svr;
+    tryAMAGICbin(mult,opASSIGN);
+    svl = sv_2num(TOPm1s);
+    svr = sv_2num(TOPs);
 #ifdef PERL_PRESERVE_IVUV
-    SvIV_please(TOPs);
-    if (SvIOK(TOPs)) {
+    SvIV_please(svr);
+    if (SvIOK(svr)) {
        /* Unless the left argument is integer in range we are going to have to
           use NV maths. Hence only attempt to coerce the right argument if
           we know the left is integer.  */
        /* Left operand is defined, so is it IV? */
-       SvIV_please(TOPm1s);
-       if (SvIOK(TOPm1s)) {
-           bool auvok = SvUOK(TOPm1s);
-           bool buvok = SvUOK(TOPs);
+       SvIV_please(svl);
+       if (SvIOK(svl)) {
+           bool auvok = SvUOK(svl);
+           bool buvok = SvUOK(svr);
            const UV topmask = (~ (UV)0) << (4 * sizeof (UV));
            const UV botmask = ~((~ (UV)0) << (4 * sizeof (UV)));
            UV alow;
@@ -1069,9 +1119,9 @@ PP(pp_multiply)
            UV bhigh;
 
            if (auvok) {
-               alow = SvUVX(TOPm1s);
+               alow = SvUVX(svl);
            } else {
-               const IV aiv = SvIVX(TOPm1s);
+               const IV aiv = SvIVX(svl);
                if (aiv >= 0) {
                    alow = aiv;
                    auvok = TRUE; /* effectively it's a UV now */
@@ -1080,9 +1130,9 @@ PP(pp_multiply)
                }
            }
            if (buvok) {
-               blow = SvUVX(TOPs);
+               blow = SvUVX(svr);
            } else {
-               const IV biv = SvIVX(TOPs);
+               const IV biv = SvIVX(svr);
                if (biv >= 0) {
                    blow = biv;
                    buvok = TRUE; /* effectively it's a UV now */
@@ -1097,7 +1147,7 @@ PP(pp_multiply)
            bhigh = blow >> (4 * sizeof (UV));
            blow &= botmask;
            if (ahigh && bhigh) {
-               /*EMPTY*/;
+               NOOP;
                /* eg 32 bit is at least 0x10000 * 0x10000 == 0x100000000
                   which is overflow. Drop to NVs below.  */
            } else if (!ahigh && !bhigh) {
@@ -1156,11 +1206,13 @@ PP(pp_multiply)
                    }
                } /* product_middle too large */
            } /* ahigh && bhigh */
-       } /* SvIOK(TOPm1s) */
-    } /* SvIOK(TOPs) */
+       } /* SvIOK(svl) */
+    } /* SvIOK(svr) */
 #endif
     {
-      dPOPTOPnnrl;
+      NV right = SvNV(svr);
+      NV left  = SvNV(svl);
+      (void)POPs;
       SETn( left * right );
       RETURN;
     }
@@ -1168,7 +1220,10 @@ PP(pp_multiply)
 
 PP(pp_divide)
 {
-    dVAR; dSP; dATARGET; tryAMAGICbin(div,opASSIGN);
+    dVAR; dSP; dATARGET; SV *svl, *svr;
+    tryAMAGICbin(div,opASSIGN);
+    svl = sv_2num(TOPm1s);
+    svr = sv_2num(TOPs);
     /* Only try to do UV divide first
        if ((SLOPPYDIVIDE is true) or
            (PERL_PRESERVE_IVUV is true and one or both SV is a UV too large
@@ -1191,20 +1246,20 @@ PP(pp_divide)
 #endif
 
 #ifdef PERL_TRY_UV_DIVIDE
-    SvIV_please(TOPs);
-    if (SvIOK(TOPs)) {
-        SvIV_please(TOPm1s);
-        if (SvIOK(TOPm1s)) {
-            bool left_non_neg = SvUOK(TOPm1s);
-            bool right_non_neg = SvUOK(TOPs);
+    SvIV_please(svr);
+    if (SvIOK(svr)) {
+        SvIV_please(svl);
+        if (SvIOK(svl)) {
+            bool left_non_neg = SvUOK(svl);
+            bool right_non_neg = SvUOK(svr);
             UV left;
             UV right;
 
             if (right_non_neg) {
-                right = SvUVX(TOPs);
+                right = SvUVX(svr);
             }
            else {
-               const IV biv = SvIVX(TOPs);
+               const IV biv = SvIVX(svr);
                 if (biv >= 0) {
                     right = biv;
                     right_non_neg = TRUE; /* effectively it's a UV now */
@@ -1222,10 +1277,10 @@ PP(pp_divide)
                 DIE(aTHX_ "Illegal division by zero");
 
             if (left_non_neg) {
-                left = SvUVX(TOPm1s);
+                left = SvUVX(svl);
             }
            else {
-               const IV aiv = SvIVX(TOPm1s);
+               const IV aiv = SvIVX(svl);
                 if (aiv >= 0) {
                     left = aiv;
                     left_non_neg = TRUE; /* effectively it's a UV now */
@@ -1273,8 +1328,14 @@ PP(pp_divide)
     } /* right wasn't SvIOK */
 #endif /* PERL_TRY_UV_DIVIDE */
     {
-       dPOPPOPnnrl;
+       NV right = SvNV(svr);
+       NV left  = SvNV(svl);
+       (void)POPs;(void)POPs;
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+       if (! Perl_isnan(right) && right == 0.0)
+#else
        if (right == 0.0)
+#endif
            DIE(aTHX_ "Illegal division by zero");
        PUSHn( left / right );
        RETURN;
@@ -1293,14 +1354,15 @@ PP(pp_modulo)
        bool dright_valid = FALSE;
        NV dright = 0.0;
        NV dleft  = 0.0;
-
-        SvIV_please(TOPs);
-        if (SvIOK(TOPs)) {
-            right_neg = !SvUOK(TOPs);
+        SV * svl;
+        SV * const svr = sv_2num(TOPs);
+        SvIV_please(svr);
+        if (SvIOK(svr)) {
+            right_neg = !SvUOK(svr);
             if (!right_neg) {
-                right = SvUVX(POPs);
+                right = SvUVX(svr);
             } else {
-               const IV biv = SvIVX(POPs);
+               const IV biv = SvIVX(svr);
                 if (biv >= 0) {
                     right = biv;
                     right_neg = FALSE; /* effectively it's a UV now */
@@ -1310,7 +1372,7 @@ PP(pp_modulo)
             }
         }
         else {
-           dright = POPn;
+           dright = SvNV(svr);
            right_neg = dright < 0;
            if (right_neg)
                dright = -dright;
@@ -1321,18 +1383,20 @@ PP(pp_modulo)
                 use_double = TRUE;
             }
        }
+       sp--;
 
         /* At this point use_double is only true if right is out of range for
            a UV.  In range NV has been rounded down to nearest UV and
            use_double false.  */
-        SvIV_please(TOPs);
-       if (!use_double && SvIOK(TOPs)) {
-            if (SvIOK(TOPs)) {
-                left_neg = !SvUOK(TOPs);
+        svl = sv_2num(TOPs);
+        SvIV_please(svl);
+       if (!use_double && SvIOK(svl)) {
+            if (SvIOK(svl)) {
+                left_neg = !SvUOK(svl);
                 if (!left_neg) {
-                    left = SvUVX(POPs);
+                    left = SvUVX(svl);
                 } else {
-                   const IV aiv = SvIVX(POPs);
+                   const IV aiv = SvIVX(svl);
                     if (aiv >= 0) {
                         left = aiv;
                         left_neg = FALSE; /* effectively it's a UV now */
@@ -1343,7 +1407,7 @@ PP(pp_modulo)
             }
         }
        else {
-           dleft = POPn;
+           dleft = SvNV(svl);
            left_neg = dleft < 0;
            if (left_neg)
                dleft = -dleft;
@@ -1371,6 +1435,7 @@ PP(pp_modulo)
                 }
             }
         }
+       sp--;
        if (use_double) {
            NV dans;
 
@@ -1439,7 +1504,7 @@ PP(pp_repeat)
              count = (IV)nv;
     }
     else
-        count = SvIVx(sv);
+        count = SvIV(sv);
     if (GIMME == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
        dMARK;
        static const char oom_list_extend[] = "Out of memory during list extend";
@@ -1505,7 +1570,7 @@ PP(pp_repeat)
                SvCUR_set(TARG, 0);
            else {
                const STRLEN max = (UV)count * len;
-               if (len > ((MEM_SIZE)~0)/count)
+               if (len > MEM_SIZE_MAX / count)
                     Perl_croak(aTHX_ oom_string_extend);
                MEM_WRAP_CHECK_1(max, char, oom_string_extend);
                SvGROW(TARG, max + 1);
@@ -1536,13 +1601,16 @@ PP(pp_repeat)
 
 PP(pp_subtract)
 {
-    dVAR; dSP; dATARGET; bool useleft; tryAMAGICbin(subtr,opASSIGN);
-    useleft = USE_LEFT(TOPm1s);
+    dVAR; dSP; dATARGET; bool useleft; SV *svl, *svr;
+    tryAMAGICbin(subtr,opASSIGN);
+    svl = sv_2num(TOPm1s);
+    svr = sv_2num(TOPs);
+    useleft = USE_LEFT(svl);
 #ifdef PERL_PRESERVE_IVUV
     /* See comments in pp_add (in pp_hot.c) about Overflow, and how
        "bad things" happen if you rely on signed integers wrapping.  */
-    SvIV_please(TOPs);
-    if (SvIOK(TOPs)) {
+    SvIV_please(svr);
+    if (SvIOK(svr)) {
        /* Unless the left argument is integer in range we are going to have to
           use NV maths. Hence only attempt to coerce the right argument if
           we know the left is integer.  */
@@ -1556,12 +1624,12 @@ PP(pp_subtract)
            /* left operand is undef, treat as zero.  */
        } else {
            /* Left operand is defined, so is it IV? */
-           SvIV_please(TOPm1s);
-           if (SvIOK(TOPm1s)) {
-               if ((auvok = SvUOK(TOPm1s)))
-                   auv = SvUVX(TOPm1s);
+           SvIV_please(svl);
+           if (SvIOK(svl)) {
+               if ((auvok = SvUOK(svl)))
+                   auv = SvUVX(svl);
                else {
-                   register const IV aiv = SvIVX(TOPm1s);
+                   register const IV aiv = SvIVX(svl);
                    if (aiv >= 0) {
                        auv = aiv;
                        auvok = 1;      /* Now acting as a sign flag.  */
@@ -1576,12 +1644,12 @@ PP(pp_subtract)
            bool result_good = 0;
            UV result;
            register UV buv;
-           bool buvok = SvUOK(TOPs);
+           bool buvok = SvUOK(svr);
        
            if (buvok)
-               buv = SvUVX(TOPs);
+               buv = SvUVX(svr);
            else {
-               register const IV biv = SvIVX(TOPs);
+               register const IV biv = SvIVX(svr);
                if (biv >= 0) {
                    buv = biv;
                    buvok = 1;
@@ -1638,15 +1706,16 @@ PP(pp_subtract)
        }
     }
 #endif
-    useleft = USE_LEFT(TOPm1s);
     {
-       dPOPnv;
+       NV value = SvNV(svr);
+       (void)POPs;
+
        if (!useleft) {
            /* left operand is undef, treat as zero - value */
            SETn(-value);
            RETURN;
        }
-       SETn( TOPn - value );
+       SETn( SvNV(svl) - value );
        RETURN;
     }
 }
@@ -2328,7 +2397,7 @@ PP(pp_negate)
 {
     dVAR; dSP; dTARGET; tryAMAGICun(neg);
     {
-       dTOPss;
+       SV * const sv = sv_2num(TOPs);
        const int flags = SvFLAGS(sv);
        SvGETMAGIC(sv);
        if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {
@@ -2433,10 +2502,12 @@ PP(pp_complement)
          UV nchar = 0;
          UV nwide = 0;
          U8 * const send = tmps + len;
+         U8 * const origtmps = tmps;
+         const UV utf8flags = UTF8_ALLOW_ANYUV;
 
          while (tmps < send) {
-           const UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
-           tmps += UTF8SKIP(tmps);
+           const UV c = utf8n_to_uvchr(tmps, send-tmps, &l, utf8flags);
+           tmps += l;
            targlen += UNISKIP(~c);
            nchar++;
            if (c > 0xff)
@@ -2444,39 +2515,38 @@ PP(pp_complement)
          }
 
          /* Now rewind strings and write them. */
-         tmps -= len;
+         tmps = origtmps;
 
          if (nwide) {
              U8 *result;
              U8 *p;
 
-             Newxz(result, targlen + 1, U8);
+             Newx(result, targlen + 1, U8);
              p = result;
              while (tmps < send) {
-                 const UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
-                 tmps += UTF8SKIP(tmps);
+                 const UV c = utf8n_to_uvchr(tmps, send-tmps, &l, utf8flags);
+                 tmps += l;
                  p = uvchr_to_utf8_flags(p, ~c, UNICODE_ALLOW_ANY);
              }
              *p = '\0';
-             sv_setpvn(TARG, (char*)result, targlen);
+             sv_usepvn_flags(TARG, (char*)result, targlen,
+                             SV_HAS_TRAILING_NUL);
              SvUTF8_on(TARG);
-             Safefree(result);
          }
          else {
              U8 *result;
              U8 *p;
 
-             Newxz(result, nchar + 1, U8);
+             Newx(result, nchar + 1, U8);
              p = result;
              while (tmps < send) {
-                 const U8 c = (U8)utf8n_to_uvchr(tmps, 0, &l, UTF8_ALLOW_ANY);
-                 tmps += UTF8SKIP(tmps);
+                 const U8 c = (U8)utf8n_to_uvchr(tmps, send-tmps, &l, utf8flags);
+                 tmps += l;
                  *p++ = ~c;
              }
              *p = '\0';
-             sv_setpvn(TARG, (char*)result, nchar);
+             sv_usepvn_flags(TARG, (char*)result, nchar, SV_HAS_TRAILING_NUL);
              SvUTF8_off(TARG);
-             Safefree(result);
          }
          SETs(TARG);
          RETURN;
@@ -2487,7 +2557,7 @@ PP(pp_complement)
            for ( ; anum && (unsigned long)tmps % sizeof(long); anum--, tmps++)
                *tmps = ~*tmps;
            tmpl = (long*)tmps;
-           for ( ; anum >= sizeof(long); anum -= sizeof(long), tmpl++)
+           for ( ; anum >= (I32)sizeof(long); anum -= (I32)sizeof(long), tmpl++)
                *tmpl = ~*tmpl;
            tmps = (U8*)tmpl;
        }
@@ -2533,8 +2603,12 @@ PP(pp_i_divide)
     }
 }
 
+#if defined(__GLIBC__) && IVSIZE == 8
 STATIC
 PP(pp_i_modulo_0)
+#else
+PP(pp_i_modulo)
+#endif
 {
      /* This is the vanilla old i_modulo. */
      dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
@@ -2554,11 +2628,12 @@ PP(pp_i_modulo_0)
 #if defined(__GLIBC__) && IVSIZE == 8
 STATIC
 PP(pp_i_modulo_1)
+
 {
      /* This is the i_modulo with the workaround for the _moddi3 bug
       * in (at least) glibc 2.2.5 (the PERL_ABS() the workaround).
       * See below for pp_i_modulo. */
-     dVAR; dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+     dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
      {
          dPOPTOPiirl;
          if (!right)
@@ -2571,7 +2646,6 @@ PP(pp_i_modulo_1)
          RETURN;
      }
 }
-#endif
 
 PP(pp_i_modulo)
 {
@@ -2591,7 +2665,6 @@ PP(pp_i_modulo)
           * opcode dispatch table if that is the case, remembering to
           * also apply the workaround so that this first round works
           * right, too.  See [perl #9402] for more information. */
-#if defined(__GLIBC__) && IVSIZE == 8
          {
               IV l =   3;
               IV r = -10;
@@ -2607,7 +2680,6 @@ PP(pp_i_modulo)
                    right = PERL_ABS(right);
               }
          }
-#endif
          /* avoid FPE_INTOVF on some platforms when left is IV_MIN */
          if (right == -1)
              SETi( 0 );
@@ -2616,6 +2688,7 @@ PP(pp_i_modulo)
          RETURN;
      }
 }
+#endif
 
 PP(pp_i_add)
 {
@@ -2825,22 +2898,24 @@ PP(pp_int)
 {
     dVAR; dSP; dTARGET; tryAMAGICun(int);
     {
-      const IV iv = TOPi; /* attempt to convert to IV if possible. */
+      SV * const sv = sv_2num(TOPs);
+      const IV iv = SvIV(sv);
       /* XXX it's arguable that compiler casting to IV might be subtly
         different from modf (for numbers inside (IV_MIN,UV_MAX)) in which
         else preferring IV has introduced a subtle behaviour change bug. OTOH
         relying on floating point to be accurate is a bug.  */
 
-      if (!SvOK(TOPs))
+      if (!SvOK(sv)) {
         SETu(0);
-      else if (SvIOK(TOPs)) {
-       if (SvIsUV(TOPs)) {
-           const UV uv = TOPu;
-           SETu(uv);
-       else
+      }
+      else if (SvIOK(sv)) {
+       if (SvIsUV(sv))
+           SETu(SvUV(sv));
+       else
            SETi(iv);
-      } else {
-         const NV value = TOPn;
+      }
+      else {
+         const NV value = SvNV(sv);
          if (value >= 0.0) {
              if (value < (NV)UV_MAX + 0.5) {
                  SETu(U_V(value));
@@ -2864,15 +2939,17 @@ PP(pp_abs)
 {
     dVAR; dSP; dTARGET; tryAMAGICun(abs);
     {
+      SV * const sv = sv_2num(TOPs);
       /* This will cache the NV value if string isn't actually integer  */
-      const IV iv = TOPi;
+      const IV iv = SvIV(sv);
 
-      if (!SvOK(TOPs))
+      if (!SvOK(sv)) {
         SETu(0);
-      else if (SvIOK(TOPs)) {
+      }
+      else if (SvIOK(sv)) {
        /* IVX is precise  */
-       if (SvIsUV(TOPs)) {
-         SETu(TOPu);   /* force it to be numeric only */
+       if (SvIsUV(sv)) {
+         SETu(SvUV(sv));       /* force it to be numeric only */
        } else {
          if (iv >= 0) {
            SETi(iv);
@@ -2887,7 +2964,7 @@ PP(pp_abs)
          }
        }
       } else{
-       const NV value = TOPn;
+       const NV value = SvNV(sv);
        if (value < 0.0)
          SETn(-value);
        else
@@ -2949,10 +3026,33 @@ PP(pp_length)
     dVAR; dSP; dTARGET;
     SV * const sv = TOPs;
 
-    if (DO_UTF8(sv))
-       SETi(sv_len_utf8(sv));
-    else
-       SETi(sv_len(sv));
+    if (SvGAMAGIC(sv)) {
+       /* For an overloaded or magic scalar, we can't know in advance if
+          it's going to be UTF-8 or not. Also, we can't call sv_len_utf8 as
+          it likes to cache the length. Maybe that should be a documented
+          feature of it.
+       */
+       STRLEN len;
+       const char *const p
+           = sv_2pv_flags(sv, &len,
+                          SV_UNDEF_RETURNS_NULL|SV_CONST_RETURN|SV_GMAGIC);
+
+       if (!p)
+           SETs(&PL_sv_undef);
+       else if (DO_UTF8(sv)) {
+           SETi(utf8_length((U8*)p, (U8*)p + len));
+       }
+       else
+           SETi(len);
+    } else if (SvOK(sv)) {
+       /* Neither magic nor overloaded.  */
+       if (DO_UTF8(sv))
+           SETi(sv_len_utf8(sv));
+       else
+           SETi(sv_len(sv));
+    } else {
+       SETs(&PL_sv_undef);
+    }
     RETURN;
 }
 
@@ -3082,7 +3182,9 @@ PP(pp_substr)
                repl = SvPV_const(repl_sv_copy, repl_len);
                repl_is_utf8 = DO_UTF8(repl_sv_copy) && SvCUR(sv);
            }
-           sv_insert(sv, pos, rem, repl, repl_len);
+           if (!SvOK(sv))
+               sv_setpvs(sv, "");
+           sv_insert_flags(sv, pos, rem, repl, repl_len, 0);
            if (repl_is_utf8)
                SvUTF8_on(sv);
            if (repl_sv_copy)
@@ -3101,15 +3203,13 @@ PP(pp_substr)
                else if (SvOK(sv))      /* is it defined ? */
                    (void)SvPOK_only_UTF8(sv);
                else
-                   sv_setpvn(sv,"",0); /* avoid lexical reincarnation */
+                   sv_setpvs(sv, ""); /* avoid lexical reincarnation */
            }
 
            if (SvTYPE(TARG) < SVt_PVLV) {
                sv_upgrade(TARG, SVt_PVLV);
                sv_magic(TARG, NULL, PERL_MAGIC_substr, NULL, 0);
            }
-           else
-               SvOK_off(TARG);
 
            LvTYPE(TARG) = 'x';
            if (LvTARG(TARG) != sv) {
@@ -3167,8 +3267,8 @@ PP(pp_index)
     STRLEN llen = 0;
     I32 offset;
     I32 retval;
-    const char *tmps;
-    const char *tmps2;
+    const char *big_p;
+    const char *little_p;
     const I32 arybase = CopARYBASE_get(PL_curcop);
     bool big_utf8;
     bool little_utf8;
@@ -3181,6 +3281,9 @@ PP(pp_index)
     }
     little = POPs;
     big = POPs;
+    big_p = SvPV_const(big, biglen);
+    little_p = SvPV_const(little, llen);
+
     big_utf8 = DO_UTF8(big);
     little_utf8 = DO_UTF8(little);
     if (big_utf8 ^ little_utf8) {
@@ -3188,9 +3291,7 @@ PP(pp_index)
        if (little_utf8 && !PL_encoding) {
            /* Well, maybe instead we might be able to downgrade the small
               string?  */
-           STRLEN little_len;
-           const U8 * const little_pv = (U8*) SvPV_const(little, little_len);
-           char * const pv = (char*)bytes_from_utf8(little_pv, &little_len,
+           char * const pv = (char*)bytes_from_utf8((U8 *)little_p, &llen,
                                                     &little_utf8);
            if (little_utf8) {
                /* If the large string is ISO-8859-1, and it's not possible to
@@ -3203,13 +3304,11 @@ PP(pp_index)
            /* At this point, pv is a malloc()ed string. So donate it to temp
               to ensure it will get free()d  */
            little = temp = newSV(0);
-           sv_usepvn(temp, pv, little_len);
+           sv_usepvn(temp, pv, llen);
+           little_p = SvPVX(little);
        } else {
-           SV * const bytes = little_utf8 ? big : little;
-           STRLEN len;
-           const char * const p = SvPV_const(bytes, len);
-
-           temp = newSVpvn(p, len);
+           temp = little_utf8
+               ? newSVpvn(big_p, biglen) : newSVpvn(little_p, llen);
 
            if (PL_encoding) {
                sv_recode_to_utf8(temp, PL_encoding);
@@ -3219,34 +3318,56 @@ PP(pp_index)
            if (little_utf8) {
                big = temp;
                big_utf8 = TRUE;
+               big_p = SvPV_const(big, biglen);
            } else {
                little = temp;
+               little_p = SvPV_const(little, llen);
            }
        }
     }
-    /* Don't actually need the NULL initialisation, but it keeps gcc quiet.  */
-    tmps2 = is_index ? NULL : SvPV_const(little, llen);
-    tmps = SvPV_const(big, biglen);
+    if (SvGAMAGIC(big)) {
+       /* Life just becomes a lot easier if I use a temporary here.
+          Otherwise I need to avoid calls to sv_pos_u2b(), which (dangerously)
+          will trigger magic and overloading again, as will fbm_instr()
+       */
+       big = newSVpvn_flags(big_p, biglen,
+                            SVs_TEMP | (big_utf8 ? SVf_UTF8 : 0));
+       big_p = SvPVX(big);
+    }
+    if (SvGAMAGIC(little) || (is_index && !SvOK(little))) {
+       /* index && SvOK() is a hack. fbm_instr() calls SvPV_const, which will
+          warn on undef, and we've already triggered a warning with the
+          SvPV_const some lines above. We can't remove that, as we need to
+          call some SvPV to trigger overloading early and find out if the
+          string is UTF-8.
+          This is all getting to messy. The API isn't quite clean enough,
+          because data access has side effects.
+       */
+       little = newSVpvn_flags(little_p, llen,
+                               SVs_TEMP | (little_utf8 ? SVf_UTF8 : 0));
+       little_p = SvPVX(little);
+    }
 
     if (MAXARG < 3)
        offset = is_index ? 0 : biglen;
     else {
        if (big_utf8 && offset > 0)
            sv_pos_u2b(big, &offset, 0);
-       offset += llen;
+       if (!is_index)
+           offset += llen;
     }
     if (offset < 0)
        offset = 0;
     else if (offset > (I32)biglen)
        offset = biglen;
-    if (!(tmps2 = is_index
-         ? fbm_instr((unsigned char*)tmps + offset,
-                     (unsigned char*)tmps + biglen, little, 0)
-         : rninstr(tmps,  tmps  + offset,
-                   tmps2, tmps2 + llen)))
+    if (!(little_p = is_index
+         ? fbm_instr((unsigned char*)big_p + offset,
+                     (unsigned char*)big_p + biglen, little, 0)
+         : rninstr(big_p,  big_p  + offset,
+                   little_p, little_p + llen)))
        retval = -1;
     else {
-       retval = tmps2 - tmps;
+       retval = little_p - big_p;
        if (retval > 0 && big_utf8)
            sv_pos_b2u(big, &retval);
     }
@@ -3260,6 +3381,8 @@ PP(pp_index)
 PP(pp_sprintf)
 {
     dVAR; dSP; dMARK; dORIGMARK; dTARGET;
+    if (SvTAINTED(MARK[1]))
+       TAINT_PROPER("sprintf");
     do_sprintf(TARG, SP-MARK, MARK+1);
     TAINT_IF(SvTAINTED(TARG));
     SP = ORIGMARK;
@@ -3270,20 +3393,20 @@ PP(pp_sprintf)
 PP(pp_ord)
 {
     dVAR; dSP; dTARGET;
+
     SV *argsv = POPs;
     STRLEN len;
     const U8 *s = (U8*)SvPV_const(argsv, len);
-    SV *tmpsv;
 
     if (PL_encoding && SvPOK(argsv) && !DO_UTF8(argsv)) {
-        tmpsv = sv_2mortal(newSVsv(argsv));
+        SV * const tmpsv = sv_2mortal(newSVsv(argsv));
         s = (U8*)sv_recode_to_utf8(tmpsv, PL_encoding);
         argsv = tmpsv;
     }
 
     XPUSHu(DO_UTF8(argsv) ?
           utf8n_to_uvchr(s, UTF8_MAXBYTES, 0, UTF8_ALLOW_ANYUV) :
-          (*s & 0xff));
+          (UV)(*s & 0xff));
 
     RETURN;
 }
@@ -3326,20 +3449,21 @@ PP(pp_chr)
     *tmps++ = (char)value;
     *tmps = '\0';
     (void)SvPOK_only(TARG);
+
     if (PL_encoding && !IN_BYTES) {
         sv_recode_to_utf8(TARG, PL_encoding);
        tmps = SvPVX(TARG);
        if (SvCUR(TARG) == 0 || !is_utf8_string((U8*)tmps, SvCUR(TARG)) ||
-           memEQ(tmps, "\xef\xbf\xbd\0", 4)) {
-           SvGROW(TARG, 3);
+           UNICODE_IS_REPLACEMENT(utf8_to_uvchr((U8*)tmps, NULL))) {
+           SvGROW(TARG, 2);
            tmps = SvPVX(TARG);
-           SvCUR_set(TARG, 2);
-           *tmps++ = (U8)UTF8_EIGHT_BIT_HI(value);
-           *tmps++ = (U8)UTF8_EIGHT_BIT_LO(value);
+           SvCUR_set(TARG, 1);
+           *tmps++ = (char)value;
            *tmps = '\0';
-           SvUTF8_on(TARG);
+           SvUTF8_off(TARG);
        }
     }
+
     XPUSHs(TARG);
     RETURN;
 }
@@ -3397,28 +3521,66 @@ PP(pp_ucfirst)
 {
     dVAR;
     dSP;
-    SV *sv = TOPs;
-    const U8 *s;
+    SV *source = TOPs;
     STRLEN slen;
+    STRLEN need;
+    SV *dest;
+    bool inplace = TRUE;
+    bool doing_utf8;
     const int op_type = PL_op->op_type;
+    const U8 *s;
+    U8 *d;
+    U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
+    STRLEN ulen;
+    STRLEN tculen;
+
+    SvGETMAGIC(source);
+    if (SvOK(source)) {
+       s = (const U8*)SvPV_nomg_const(source, slen);
+    } else {
+       if (ckWARN(WARN_UNINITIALIZED))
+           report_uninit(source);
+       s = (const U8*)"";
+       slen = 0;
+    }
 
-    SvGETMAGIC(sv);
-    if (DO_UTF8(sv) &&
-       (s = (const U8*)SvPV_nomg_const(sv, slen)) && slen &&
-       UTF8_IS_START(*s)) {
-       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
-       STRLEN ulen;
-       STRLEN tculen;
-
+    if (slen && DO_UTF8(source) && UTF8_IS_START(*s)) {
+       doing_utf8 = TRUE;
        utf8_to_uvchr(s, &ulen);
        if (op_type == OP_UCFIRST) {
            toTITLE_utf8(s, tmpbuf, &tculen);
        } else {
            toLOWER_utf8(s, tmpbuf, &tculen);
        }
+       /* If the two differ, we definately cannot do inplace.  */
+       inplace = (ulen == tculen);
+       need = slen + 1 - ulen + tculen;
+    } else {
+       doing_utf8 = FALSE;
+       need = slen + 1;
+    }
 
-       if (!SvPADTMP(sv) || SvREADONLY(sv) || ulen != tculen) {
-           dTARGET;
+    if (SvPADTMP(source) && !SvREADONLY(source) && inplace && SvTEMP(source)) {
+       /* We can convert in place.  */
+
+       dest = source;
+       s = d = (U8*)SvPV_force_nomg(source, slen);
+    } else {
+       dTARGET;
+
+       dest = TARG;
+
+       SvUPGRADE(dest, SVt_PV);
+       d = (U8*)SvGROW(dest, need);
+       (void)SvPOK_only(dest);
+
+       SETs(dest);
+
+       inplace = FALSE;
+    }
+
+    if (doing_utf8) {
+       if(!inplace) {
            /* slen is the byte length of the whole SV.
             * ulen is the byte length of the original Unicode character
             * stored as UTF-8 at s.
@@ -3426,127 +3588,146 @@ PP(pp_ucfirst)
             * lowercased) Unicode character stored as UTF-8 at tmpbuf.
             * We first set the result to be the titlecased (/lowercased)
             * character, and then append the rest of the SV data. */
-           sv_setpvn(TARG, (char*)tmpbuf, tculen);
+           sv_setpvn(dest, (char*)tmpbuf, tculen);
            if (slen > ulen)
-               sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
-           SvUTF8_on(TARG);
-           sv = TARG;
-           SETs(sv);
+               sv_catpvn(dest, (char*)(s + ulen), slen - ulen);
+           SvUTF8_on(dest);
        }
        else {
-           s = (U8*)SvPV_force_nomg(sv, slen);
-           Copy(tmpbuf, s, tculen, U8);
+           Copy(tmpbuf, d, tculen, U8);
+           SvCUR_set(dest, need - 1);
        }
     }
     else {
-       U8 *s1;
-       if (!SvPADTMP(sv) || SvREADONLY(sv)) {
-           dTARGET;
-           SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv_nomg(TARG, sv);
-           sv = TARG;
-           SETs(sv);
-       }
-       s1 = (U8*)SvPV_force_nomg(sv, slen);
-       if (*s1) {
+       if (*s) {
            if (IN_LOCALE_RUNTIME) {
                TAINT;
-               SvTAINTED_on(sv);
-               *s1 = (op_type == OP_UCFIRST)
-                   ? toUPPER_LC(*s1) : toLOWER_LC(*s1);
+               SvTAINTED_on(dest);
+               *d = (op_type == OP_UCFIRST)
+                   ? toUPPER_LC(*s) : toLOWER_LC(*s);
            }
            else
-               *s1 = (op_type == OP_UCFIRST) ? toUPPER(*s1) : toLOWER(*s1);
+               *d = (op_type == OP_UCFIRST) ? toUPPER(*s) : toLOWER(*s);
+       } else {
+           /* See bug #39028  */
+           *d = *s;
+       }
+
+       if (SvUTF8(source))
+           SvUTF8_on(dest);
+
+       if (!inplace) {
+           /* This will copy the trailing NUL  */
+           Copy(s + 1, d + 1, slen, U8);
+           SvCUR_set(dest, need - 1);
        }
     }
-    SvSETMAGIC(sv);
+    SvSETMAGIC(dest);
     RETURN;
 }
 
+/* There's so much setup/teardown code common between uc and lc, I wonder if
+   it would be worth merging the two, and just having a switch outside each
+   of the three tight loops.  */
 PP(pp_uc)
 {
     dVAR;
     dSP;
-    SV *sv = TOPs;
+    SV *source = TOPs;
     STRLEN len;
+    STRLEN min;
+    SV *dest;
+    const U8 *s;
+    U8 *d;
 
-    SvGETMAGIC(sv);
-    if (DO_UTF8(sv)) {
+    SvGETMAGIC(source);
+
+    if (SvPADTMP(source) && !SvREADONLY(source) && !SvAMAGIC(source)
+       && SvTEMP(source) && !DO_UTF8(source)) {
+       /* We can convert in place.  */
+
+       dest = source;
+       s = d = (U8*)SvPV_force_nomg(source, len);
+       min = len + 1;
+    } else {
        dTARGET;
-       STRLEN ulen;
-       register U8 *d;
-       const U8 *s;
-       const U8 *send;
-       U8 tmpbuf[UTF8_MAXBYTES+1];
 
-       s = (const U8*)SvPV_nomg_const(sv,len);
-       if (!len) {
-           SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setpvn(TARG, "", 0);
-           sv = TARG;
-           SETs(sv);
-       }
-       else {
-           STRLEN min = len + 1;
+       dest = TARG;
 
-           SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, min);
-           (void)SvPOK_only(TARG);
-           d = (U8*)SvPVX(TARG);
-           send = s + len;
-           while (s < send) {
-               STRLEN u = UTF8SKIP(s);
-
-               toUPPER_utf8(s, tmpbuf, &ulen);
-               if (ulen > u && (SvLEN(TARG) < (min += ulen - u))) {
-                   /* If the eventually required minimum size outgrows
-                    * the available space, we need to grow. */
-                   const UV o = d - (U8*)SvPVX_const(TARG);
-
-                   /* If someone uppercases one million U+03B0s we
-                    * SvGROW() one million times.  Or we could try
-                    * guessing how much to allocate without allocating
-                    * too much. Such is life. */
-                   SvGROW(TARG, min);
-                   d = (U8*)SvPVX(TARG) + o;
-               }
-               Copy(tmpbuf, d, ulen, U8);
-               d += ulen;
-               s += u;
-           }
-           *d = '\0';
-           SvUTF8_on(TARG);
-           SvCUR_set(TARG, d - (U8*)SvPVX_const(TARG));
-           sv = TARG;
-           SETs(sv);
+       /* The old implementation would copy source into TARG at this point.
+          This had the side effect that if source was undef, TARG was now
+          an undefined SV with PADTMP set, and they don't warn inside
+          sv_2pv_flags(). However, we're now getting the PV direct from
+          source, which doesn't have PADTMP set, so it would warn. Hence the
+          little games.  */
+
+       if (SvOK(source)) {
+           s = (const U8*)SvPV_nomg_const(source, len);
+       } else {
+           if (ckWARN(WARN_UNINITIALIZED))
+               report_uninit(source);
+           s = (const U8*)"";
+           len = 0;
        }
+       min = len + 1;
+
+       SvUPGRADE(dest, SVt_PV);
+       d = (U8*)SvGROW(dest, min);
+       (void)SvPOK_only(dest);
+
+       SETs(dest);
     }
-    else {
-       U8 *s;
-       if (!SvPADTMP(sv) || SvREADONLY(sv)) {
-           dTARGET;
-           SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv_nomg(TARG, sv);
-           sv = TARG;
-           SETs(sv);
+
+    /* Overloaded values may have toggled the UTF-8 flag on source, so we need
+       to check DO_UTF8 again here.  */
+
+    if (DO_UTF8(source)) {
+       const U8 *const send = s + len;
+       U8 tmpbuf[UTF8_MAXBYTES+1];
+
+       while (s < send) {
+           const STRLEN u = UTF8SKIP(s);
+           STRLEN ulen;
+
+           toUPPER_utf8(s, tmpbuf, &ulen);
+           if (ulen > u && (SvLEN(dest) < (min += ulen - u))) {
+               /* If the eventually required minimum size outgrows
+                * the available space, we need to grow. */
+               const UV o = d - (U8*)SvPVX_const(dest);
+
+               /* If someone uppercases one million U+03B0s we SvGROW() one
+                * million times.  Or we could try guessing how much to
+                allocate without allocating too much.  Such is life. */
+               SvGROW(dest, min);
+               d = (U8*)SvPVX(dest) + o;
+           }
+           Copy(tmpbuf, d, ulen, U8);
+           d += ulen;
+           s += u;
        }
-       s = (U8*)SvPV_force_nomg(sv, len);
+       SvUTF8_on(dest);
+       *d = '\0';
+       SvCUR_set(dest, d - (U8*)SvPVX_const(dest));
+    } else {
        if (len) {
-           register const U8 *send = s + len;
-
+           const U8 *const send = s + len;
            if (IN_LOCALE_RUNTIME) {
                TAINT;
-               SvTAINTED_on(sv);
-               for (; s < send; s++)
-                   *s = toUPPER_LC(*s);
+               SvTAINTED_on(dest);
+               for (; s < send; d++, s++)
+                   *d = toUPPER_LC(*s);
            }
            else {
-               for (; s < send; s++)
-                   *s = toUPPER(*s);
+               for (; s < send; d++, s++)
+                   *d = toUPPER(*s);
            }
        }
+       if (source != dest) {
+           *d = '\0';
+           SvCUR_set(dest, d - (U8*)SvPVX_const(dest));
+       }
     }
-    SvSETMAGIC(sv);
+    SvSETMAGIC(dest);
     RETURN;
 }
 
@@ -3554,107 +3735,117 @@ PP(pp_lc)
 {
     dVAR;
     dSP;
-    SV *sv = TOPs;
+    SV *source = TOPs;
     STRLEN len;
+    STRLEN min;
+    SV *dest;
+    const U8 *s;
+    U8 *d;
 
-    SvGETMAGIC(sv);
-    if (DO_UTF8(sv)) {
+    SvGETMAGIC(source);
+
+    if (SvPADTMP(source) && !SvREADONLY(source) && !SvAMAGIC(source)
+       && SvTEMP(source) && !DO_UTF8(source)) {
+       /* We can convert in place.  */
+
+       dest = source;
+       s = d = (U8*)SvPV_force_nomg(source, len);
+       min = len + 1;
+    } else {
        dTARGET;
-       const U8 *s;
-       STRLEN ulen;
-       register U8 *d;
-       const U8 *send;
-       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
 
-       s = (const U8*)SvPV_nomg_const(sv,len);
-       if (!len) {
-           SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setpvn(TARG, "", 0);
-           sv = TARG;
-           SETs(sv);
+       dest = TARG;
+
+       /* The old implementation would copy source into TARG at this point.
+          This had the side effect that if source was undef, TARG was now
+          an undefined SV with PADTMP set, and they don't warn inside
+          sv_2pv_flags(). However, we're now getting the PV direct from
+          source, which doesn't have PADTMP set, so it would warn. Hence the
+          little games.  */
+
+       if (SvOK(source)) {
+           s = (const U8*)SvPV_nomg_const(source, len);
+       } else {
+           if (ckWARN(WARN_UNINITIALIZED))
+               report_uninit(source);
+           s = (const U8*)"";
+           len = 0;
        }
-       else {
-           STRLEN min = len + 1;
+       min = len + 1;
 
-           SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, min);
-           (void)SvPOK_only(TARG);
-           d = (U8*)SvPVX(TARG);
-           send = s + len;
-           while (s < send) {
-               const STRLEN u = UTF8SKIP(s);
-               const UV uv = toLOWER_utf8(s, tmpbuf, &ulen);
+       SvUPGRADE(dest, SVt_PV);
+       d = (U8*)SvGROW(dest, min);
+       (void)SvPOK_only(dest);
+
+       SETs(dest);
+    }
+
+    /* Overloaded values may have toggled the UTF-8 flag on source, so we need
+       to check DO_UTF8 again here.  */
+
+    if (DO_UTF8(source)) {
+       const U8 *const send = s + len;
+       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
+
+       while (s < send) {
+           const STRLEN u = UTF8SKIP(s);
+           STRLEN ulen;
+           const UV uv = toLOWER_utf8(s, tmpbuf, &ulen);
 
 #define GREEK_CAPITAL_LETTER_SIGMA 0x03A3 /* Unicode U+03A3 */
-               if (uv == GREEK_CAPITAL_LETTER_SIGMA) {
-                   /*EMPTY*/
-                    /*
-                     * Now if the sigma is NOT followed by
-                     * /$ignorable_sequence$cased_letter/;
-                     * and it IS preceded by
-                     * /$cased_letter$ignorable_sequence/;
-                     * where $ignorable_sequence is
-                     * [\x{2010}\x{AD}\p{Mn}]*
-                     * and $cased_letter is
-                     * [\p{Ll}\p{Lo}\p{Lt}]
-                     * then it should be mapped to 0x03C2,
-                     * (GREEK SMALL LETTER FINAL SIGMA),
-                     * instead of staying 0x03A3.
-                     * "should be": in other words,
-                     * this is not implemented yet.
-                     * See lib/unicore/SpecialCasing.txt.
-                     */
-               }
-               if (ulen > u && (SvLEN(TARG) < (min += ulen - u))) {
-                   /* If the eventually required minimum size outgrows
-                    * the available space, we need to grow. */
-                   const UV o = d - (U8*)SvPVX_const(TARG);
-
-                   /* If someone lowercases one million U+0130s we
-                    * SvGROW() one million times.  Or we could try
-                    * guessing how much to allocate without allocating.
-                    * too much.  Such is life. */
-                   SvGROW(TARG, min);
-                   d = (U8*)SvPVX(TARG) + o;
-               }
-               Copy(tmpbuf, d, ulen, U8);
-               d += ulen;
-               s += u;
+           if (uv == GREEK_CAPITAL_LETTER_SIGMA) {
+               NOOP;
+               /*
+                * Now if the sigma is NOT followed by
+                * /$ignorable_sequence$cased_letter/;
+                * and it IS preceded by /$cased_letter$ignorable_sequence/;
+                * where $ignorable_sequence is [\x{2010}\x{AD}\p{Mn}]*
+                * and $cased_letter is [\p{Ll}\p{Lo}\p{Lt}]
+                * then it should be mapped to 0x03C2,
+                * (GREEK SMALL LETTER FINAL SIGMA),
+                * instead of staying 0x03A3.
+                * "should be": in other words, this is not implemented yet.
+                * See lib/unicore/SpecialCasing.txt.
+                */
            }
-           *d = '\0';
-           SvUTF8_on(TARG);
-           SvCUR_set(TARG, d - (U8*)SvPVX_const(TARG));
-           sv = TARG;
-           SETs(sv);
-       }
-    }
-    else {
-       U8 *s;
-       if (!SvPADTMP(sv) || SvREADONLY(sv)) {
-           dTARGET;
-           SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv_nomg(TARG, sv);
-           sv = TARG;
-           SETs(sv);
+           if (ulen > u && (SvLEN(dest) < (min += ulen - u))) {
+               /* If the eventually required minimum size outgrows
+                * the available space, we need to grow. */
+               const UV o = d - (U8*)SvPVX_const(dest);
+
+               /* If someone lowercases one million U+0130s we SvGROW() one
+                * million times.  Or we could try guessing how much to
+                allocate without allocating too much.  Such is life. */
+               SvGROW(dest, min);
+               d = (U8*)SvPVX(dest) + o;
+           }
+           Copy(tmpbuf, d, ulen, U8);
+           d += ulen;
+           s += u;
        }
-
-       s = (U8*)SvPV_force_nomg(sv, len);
+       SvUTF8_on(dest);
+       *d = '\0';
+       SvCUR_set(dest, d - (U8*)SvPVX_const(dest));
+    } else {
        if (len) {
-           register const U8 * const send = s + len;
-
+           const U8 *const send = s + len;
            if (IN_LOCALE_RUNTIME) {
                TAINT;
-               SvTAINTED_on(sv);
-               for (; s < send; s++)
-                   *s = toLOWER_LC(*s);
+               SvTAINTED_on(dest);
+               for (; s < send; d++, s++)
+                   *d = toLOWER_LC(*s);
            }
            else {
-               for (; s < send; s++)
-                   *s = toLOWER(*s);
+               for (; s < send; d++, s++)
+                   *d = toLOWER(*s);
            }
        }
+       if (source != dest) {
+           *d = '\0';
+           SvCUR_set(dest, d - (U8*)SvPVX_const(dest));
+       }
     }
-    SvSETMAGIC(sv);
+    SvSETMAGIC(dest);
     RETURN;
 }
 
@@ -3723,7 +3914,7 @@ PP(pp_aslice)
            register SV **svp;
            I32 max = -1;
            for (svp = MARK + 1; svp <= SP; svp++) {
-               const I32 elem = SvIVx(*svp);
+               const I32 elem = SvIV(*svp);
                if (elem > max)
                    max = elem;
            }
@@ -3732,7 +3923,7 @@ PP(pp_aslice)
        }
        while (++MARK <= SP) {
            register SV **svp;
-           I32 elem = SvIVx(*MARK);
+           I32 elem = SvIV(*MARK);
 
            if (elem > 0)
                elem -= arybase;
@@ -3754,13 +3945,74 @@ PP(pp_aslice)
     RETURN;
 }
 
+PP(pp_aeach)
+{
+    dVAR;
+    dSP;
+    AV *array = (AV*)POPs;
+    const I32 gimme = GIMME_V;
+    IV *iterp = Perl_av_iter_p(aTHX_ array);
+    const IV current = (*iterp)++;
+
+    if (current > av_len(array)) {
+       *iterp = 0;
+       if (gimme == G_SCALAR)
+           RETPUSHUNDEF;
+       else
+           RETURN;
+    }
+
+    EXTEND(SP, 2);
+    mPUSHi(CopARYBASE_get(PL_curcop) + current);
+    if (gimme == G_ARRAY) {
+       SV **const element = av_fetch(array, current, 0);
+        PUSHs(element ? *element : &PL_sv_undef);
+    }
+    RETURN;
+}
+
+PP(pp_akeys)
+{
+    dVAR;
+    dSP;
+    AV *array = (AV*)POPs;
+    const I32 gimme = GIMME_V;
+
+    *Perl_av_iter_p(aTHX_ array) = 0;
+
+    if (gimme == G_SCALAR) {
+       dTARGET;
+       PUSHi(av_len(array) + 1);
+    }
+    else if (gimme == G_ARRAY) {
+        IV n = Perl_av_len(aTHX_ array);
+        IV i = CopARYBASE_get(PL_curcop);
+
+        EXTEND(SP, n + 1);
+
+       if (PL_op->op_type == OP_AKEYS) {
+           n += i;
+           for (;  i <= n;  i++) {
+               mPUSHi(i);
+           }
+       }
+       else {
+           for (i = 0;  i <= n;  i++) {
+               SV *const *const elem = Perl_av_fetch(aTHX_ array, i, 0);
+               PUSHs(elem ? *elem : &PL_sv_undef);
+           }
+       }
+    }
+    RETURN;
+}
+
 /* Associative arrays. */
 
 PP(pp_each)
 {
     dVAR;
     dSP;
-    HV * const hash = (HV*)POPs;
+    HV * hash = MUTABLE_HV(POPs);
     HE *entry;
     const I32 gimme = GIMME_V;
 
@@ -3797,7 +4049,7 @@ PP(pp_delete)
 
     if (PL_op->op_private & OPpSLICE) {
        dMARK; dORIGMARK;
-       HV * const hv = (HV*)POPs;
+       HV * const hv = MUTABLE_HV(POPs);
        const U32 hvtype = SvTYPE(hv);
        if (hvtype == SVt_PVHV) {                       /* hash element */
            while (++MARK <= SP) {
@@ -3828,7 +4080,7 @@ PP(pp_delete)
     }
     else {
        SV *keysv = POPs;
-       HV * const hv = (HV*)POPs;
+       HV * const hv = MUTABLE_HV(POPs);
        SV *sv;
        if (SvTYPE(hv) == SVt_PVHV)
            sv = hv_delete_ent(hv, keysv, discard, 0);
@@ -3866,7 +4118,7 @@ PP(pp_exists)
        RETPUSHNO;
     }
     tmpsv = POPs;
-    hv = (HV*)POPs;
+    hv = MUTABLE_HV(POPs);
     if (SvTYPE(hv) == SVt_PVHV) {
        if (hv_exists_ent(hv, tmpsv, 0))
            RETPUSHYES;
@@ -3886,7 +4138,7 @@ PP(pp_exists)
 PP(pp_hslice)
 {
     dVAR; dSP; dMARK; dORIGMARK;
-    register HV * const hv = (HV*)POPs;
+    register HV * const hv = MUTABLE_HV(POPs);
     register const I32 lval = (PL_op->op_flags & OPf_MOD || LVRET);
     const bool localizing = PL_op->op_private & OPpLVAL_INTRO;
     bool other_magic = FALSE;
@@ -3917,11 +4169,11 @@ PP(pp_hslice)
         }
 
         he = hv_fetch_ent(hv, keysv, lval, 0);
-        svp = he ? &HeVAL(he) : 0;
+        svp = he ? &HeVAL(he) : NULL;
 
         if (lval) {
             if (!svp || *svp == &PL_sv_undef) {
-                DIE(aTHX_ PL_no_helem_sv, keysv);
+                DIE(aTHX_ PL_no_helem_sv, SVfARG(keysv));
             }
             if (localizing) {
                if (HvNAME_get(hv) && isGV(*svp))
@@ -3933,7 +4185,7 @@ PP(pp_hslice)
                        STRLEN keylen;
                        const char * const key = SvPV_const(keysv, keylen);
                        SAVEDELETE(hv, savepvn(key,keylen),
-                                  SvUTF8(keysv) ? -keylen : keylen);
+                                  SvUTF8(keysv) ? -(I32)keylen : (I32)keylen);
                    }
                }
             }
@@ -3972,13 +4224,13 @@ PP(pp_lslice)
     SV ** const firstlelem = PL_stack_base + POPMARK + 1;
     register SV ** const firstrelem = lastlelem + 1;
     const I32 arybase = CopARYBASE_get(PL_curcop);
-    I32 is_something_there = PL_op->op_flags & OPf_MOD;
+    I32 is_something_there = FALSE;
 
     register const I32 max = lastrelem - lastlelem;
     register SV **lelem;
 
     if (GIMME != G_ARRAY) {
-       I32 ix = SvIVx(*lastlelem);
+       I32 ix = SvIV(*lastlelem);
        if (ix < 0)
            ix += max;
        else
@@ -3997,7 +4249,7 @@ PP(pp_lslice)
     }
 
     for (lelem = firstlelem; lelem <= lastlelem; lelem++) {
-       I32 ix = SvIVx(*lelem);
+       I32 ix = SvIV(*lelem);
        if (ix < 0)
            ix += max;
        else
@@ -4021,16 +4273,17 @@ PP(pp_anonlist)
 {
     dVAR; dSP; dMARK; dORIGMARK;
     const I32 items = SP - MARK;
-    SV * const av = sv_2mortal((SV*)av_make(items, MARK+1));
+    SV * const av = (SV *) av_make(items, MARK+1);
     SP = ORIGMARK;             /* av_make() might realloc stack_sp */
-    XPUSHs(av);
+    mXPUSHs((PL_op->op_flags & OPf_SPECIAL)
+           ? newRV_noinc(av) : av);
     RETURN;
 }
 
 PP(pp_anonhash)
 {
     dVAR; dSP; dMARK; dORIGMARK;
-    HV* const hv = (HV*)sv_2mortal((SV*)newHV());
+    HV* const hv = newHV();
 
     while (MARK < SP) {
        SV * const key = *++MARK;
@@ -4042,7 +4295,8 @@ PP(pp_anonhash)
        (void)hv_store_ent(hv,key,val,0);
     }
     SP = ORIGMARK;
-    XPUSHs((SV*)hv);
+    mXPUSHs((PL_op->op_flags & OPf_SPECIAL)
+           ? newRV_noinc((SV*) hv) : (SV*) hv);
     RETURN;
 }
 
@@ -4074,7 +4328,7 @@ PP(pp_splice)
     SP++;
 
     if (++MARK < SP) {
-       offset = i = SvIVx(*MARK);
+       offset = i = SvIV(*MARK);
        if (offset < 0)
            offset += AvFILLp(ary) + 1;
        else
@@ -4123,7 +4377,7 @@ PP(pp_splice)
     }
 
     if (diff < 0) {                            /* shrinking the area */
-       SV **tmparyval;
+       SV **tmparyval = NULL;
        if (newlen) {
            Newx(tmparyval, newlen, SV*);       /* so remember insertion */
            Copy(MARK, tmparyval, newlen, SV*);
@@ -4162,7 +4416,7 @@ PP(pp_splice)
                    *dst-- = *src--;
            }
            dst = AvARRAY(ary);
-           SvPV_set(ary, (char*)(AvARRAY(ary) - diff)); /* diff is negative */
+           AvARRAY(ary) = AvARRAY(ary) - diff; /* diff is negative */
            AvMAX(ary) += diff;
        }
        else {
@@ -4198,7 +4452,7 @@ PP(pp_splice)
                    dst = src - diff;
                    Move(src, dst, offset, SV*);
                }
-               SvPV_set(ary, (char*)(AvARRAY(ary) - diff));/* diff is positive */
+               AvARRAY(ary) = AvARRAY(ary) - diff;/* diff is positive */
                AvMAX(ary) += diff;
                AvFILLp(ary) += diff;
            }
@@ -4254,7 +4508,7 @@ PP(pp_splice)
 PP(pp_push)
 {
     dVAR; dSP; dMARK; dORIGMARK; dTARGET;
-    register AV *ary = (AV*)*++MARK;
+    register AV * const ary = (AV*)*++MARK;
     const MAGIC * const mg = SvTIED_mg((SV*)ary, PERL_MAGIC_tied);
 
     if (mg) {
@@ -4269,12 +4523,17 @@ PP(pp_push)
        PUSHi( AvFILL(ary) + 1 );
     }
     else {
+       PL_delaymagic = DM_DELAY;
        for (++MARK; MARK <= SP; MARK++) {
            SV * const sv = newSV(0);
            if (*MARK)
                sv_setsv(sv, *MARK);
            av_store(ary, AvFILLp(ary)+1, sv);
        }
+       if (PL_delaymagic & DM_ARRAY)
+           mg_set((SV*)ary);
+
+       PL_delaymagic = 0;
        SP = ORIGMARK;
        PUSHi( AvFILLp(ary) + 1 );
     }
@@ -4344,18 +4603,23 @@ PP(pp_reverse)
        register I32 tmp;
        dTARGET;
        STRLEN len;
-       I32 padoff_du;
+       PADOFFSET padoff_du;
 
        SvUTF8_off(TARG);                               /* decontaminate */
        if (SP - MARK > 1)
            do_join(TARG, &PL_sv_no, MARK, SP);
-       else
+       else {
            sv_setsv(TARG, (SP > MARK)
                    ? *SP
                    : (padoff_du = find_rundefsvoffset(),
                        (padoff_du == NOT_IN_PAD
                         || PAD_COMPNAME_FLAGS_isOUR(padoff_du))
                        ? DEFSV : PAD_SVl(padoff_du)));
+
+           if (! SvOK(TARG) && ckWARN(WARN_UNINITIALIZED))
+               report_uninit(TARG);
+       }
+
        up = SvPV_force(TARG, len);
        if (len > 1) {
            if (DO_UTF8(TARG)) {        /* first reverse each character */
@@ -4411,7 +4675,7 @@ PP(pp_split)
     register SV *dstr;
     register const char *m;
     I32 iters = 0;
-    const STRLEN slen = do_utf8 ? utf8_length((U8*)s, (U8*)strend) : (strend - s);
+    const STRLEN slen = do_utf8 ? utf8_length((U8*)s, (U8*)strend) : (STRLEN)(strend - s);
     I32 maxiters = slen + 10;
     const char *orig;
     const I32 origlimit = limit;
@@ -4419,7 +4683,7 @@ PP(pp_split)
     I32 base;
     const I32 gimme = GIMME_V;
     const I32 oldsave = PL_savestack_ix;
-    I32 make_mortal = 1;
+    U32 make_mortal = SVs_TEMP;
     bool multiline = 0;
     MAGIC *mg = NULL;
 
@@ -4432,18 +4696,20 @@ PP(pp_split)
        DIE(aTHX_ "panic: pp_split");
     rx = PM_GETRE(pm);
 
-    TAINT_IF((pm->op_pmflags & PMf_LOCALE) &&
-            (pm->op_pmflags & (PMf_WHITE | PMf_SKIPWHITE)));
+    TAINT_IF((RX_EXTFLAGS(rx) & RXf_PMf_LOCALE) &&
+            (RX_EXTFLAGS(rx) & (RXf_WHITE | RXf_SKIPWHITE)));
 
     RX_MATCH_UTF8_set(rx, do_utf8);
 
-    if (pm->op_pmreplroot) {
 #ifdef USE_ITHREADS
-       ary = GvAVn((GV*)PAD_SVl(INT2PTR(PADOFFSET, pm->op_pmreplroot)));
+    if (pm->op_pmreplrootu.op_pmtargetoff) {
+       ary = GvAVn((GV*)PAD_SVl(pm->op_pmreplrootu.op_pmtargetoff));
+    }
 #else
-       ary = GvAVn((GV*)pm->op_pmreplroot);
-#endif
+    if (pm->op_pmreplrootu.op_pmtargetgv) {
+       ary = GvAVn(pm->op_pmreplrootu.op_pmtargetgv);
     }
+#endif
     else if (gimme != G_ARRAY)
        ary = GvAVn(PL_defgv);
     else
@@ -4473,8 +4739,12 @@ PP(pp_split)
     }
     base = SP - PL_stack_base;
     orig = s;
-    if (pm->op_pmflags & PMf_SKIPWHITE) {
-       if (pm->op_pmflags & PMf_LOCALE) {
+    if (RX_EXTFLAGS(rx) & RXf_SKIPWHITE) {
+       if (do_utf8) {
+           while (*s == ' ' || is_utf8_space((U8*)s))
+               s += UTF8SKIP(s);
+       }
+       else if (RX_EXTFLAGS(rx) & RXf_PMf_LOCALE) {
            while (isSPACE_LC(*s))
                s++;
        }
@@ -4483,72 +4753,131 @@ PP(pp_split)
                s++;
        }
     }
-    if (pm->op_pmflags & PMf_MULTILINE) {
+    if (RX_EXTFLAGS(rx) & PMf_MULTILINE) {
        multiline = 1;
     }
 
     if (!limit)
        limit = maxiters + 2;
-    if (pm->op_pmflags & PMf_WHITE) {
+    if (RX_EXTFLAGS(rx) & RXf_WHITE) {
        while (--limit) {
            m = s;
-           while (m < strend &&
-                  !((pm->op_pmflags & PMf_LOCALE)
-                    ? isSPACE_LC(*m) : isSPACE(*m)))
-               ++m;
+           /* this one uses 'm' and is a negative test */
+           if (do_utf8) {
+               while (m < strend && !( *m == ' ' || is_utf8_space((U8*)m) )) {
+                   const int t = UTF8SKIP(m);
+                   /* is_utf8_space returns FALSE for malform utf8 */
+                   if (strend - m < t)
+                       m = strend;
+                   else
+                       m += t;
+               }
+            } else if (RX_EXTFLAGS(rx) & RXf_PMf_LOCALE) {
+               while (m < strend && !isSPACE_LC(*m))
+                   ++m;
+            } else {
+                while (m < strend && !isSPACE(*m))
+                    ++m;
+            }  
            if (m >= strend)
                break;
 
-           dstr = newSVpvn(s, m-s);
-           if (make_mortal)
-               sv_2mortal(dstr);
-           if (do_utf8)
-               (void)SvUTF8_on(dstr);
+           dstr = newSVpvn_flags(s, m-s,
+                                 (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
            XPUSHs(dstr);
 
-           s = m + 1;
-           while (s < strend &&
-                  ((pm->op_pmflags & PMf_LOCALE)
-                   ? isSPACE_LC(*s) : isSPACE(*s)))
-               ++s;
+           /* skip the whitespace found last */
+           if (do_utf8)
+               s = m + UTF8SKIP(m);
+           else
+               s = m + 1;
+
+           /* this one uses 's' and is a positive test */
+           if (do_utf8) {
+               while (s < strend && ( *s == ' ' || is_utf8_space((U8*)s) ))
+                   s +=  UTF8SKIP(s);
+            } else if (RX_EXTFLAGS(rx) & RXf_PMf_LOCALE) {
+               while (s < strend && isSPACE_LC(*s))
+                   ++s;
+            } else {
+                while (s < strend && isSPACE(*s))
+                    ++s;
+            }      
        }
     }
-    else if (rx->precomp[0] == '^' && rx->precomp[1] == '\0') {
+    else if (RX_EXTFLAGS(rx) & RXf_START_ONLY) {
        while (--limit) {
            for (m = s; m < strend && *m != '\n'; m++)
                ;
            m++;
            if (m >= strend)
                break;
-           dstr = newSVpvn(s, m-s);
-           if (make_mortal)
-               sv_2mortal(dstr);
-           if (do_utf8)
-               (void)SvUTF8_on(dstr);
+           dstr = newSVpvn_flags(s, m-s,
+                                 (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
            XPUSHs(dstr);
            s = m;
        }
     }
-    else if (do_utf8 == ((rx->reganch & ROPT_UTF8) != 0) &&
-            (rx->reganch & RE_USE_INTUIT) && !rx->nparens
-            && (rx->reganch & ROPT_CHECK_ALL)
-            && !(rx->reganch & ROPT_ANCH)) {
-       const int tail = (rx->reganch & RE_INTUIT_TAIL);
-       SV * const csv = CALLREG_INTUIT_STRING(aTHX_ rx);
+    else if (RX_EXTFLAGS(rx) & RXf_NULL && !(s >= strend)) {
+        /*
+          Pre-extend the stack, either the number of bytes or
+          characters in the string or a limited amount, triggered by:
+
+          my ($x, $y) = split //, $str;
+            or
+          split //, $str, $i;
+        */
+        const U32 items = limit - 1; 
+        if (items < slen)
+            EXTEND(SP, items);
+        else
+            EXTEND(SP, slen);
+
+        if (do_utf8) {
+            while (--limit) {
+                /* keep track of how many bytes we skip over */
+                m = s;
+                s += UTF8SKIP(s);
+                dstr = newSVpvn_flags(m, s-m, SVf_UTF8 | make_mortal);
+
+                PUSHs(dstr);
+
+                if (s >= strend)
+                    break;
+            }
+        } else {
+            while (--limit) {
+                dstr = newSVpvn(s, 1);
+
+                s++;
+
+                if (make_mortal)
+                    sv_2mortal(dstr);
+
+                PUSHs(dstr);
+
+                if (s >= strend)
+                    break;
+            }
+        }
+    }
+    else if (do_utf8 == (RX_UTF8(rx) != 0) &&
+            (RX_EXTFLAGS(rx) & RXf_USE_INTUIT) && !RX_NPARENS(rx)
+            && (RX_EXTFLAGS(rx) & RXf_CHECK_ALL)
+            && !(RX_EXTFLAGS(rx) & RXf_ANCH)) {
+       const int tail = (RX_EXTFLAGS(rx) & RXf_INTUIT_TAIL);
+       SV * const csv = CALLREG_INTUIT_STRING(rx);
 
-       len = rx->minlen;
-       if (len == 1 && !(rx->reganch & ROPT_UTF8) && !tail) {
+       len = RX_MINLENRET(rx);
+       if (len == 1 && !RX_UTF8(rx) && !tail) {
            const char c = *SvPV_nolen_const(csv);
            while (--limit) {
                for (m = s; m < strend && *m != c; m++)
                    ;
                if (m >= strend)
                    break;
-               dstr = newSVpvn(s, m-s);
-               if (make_mortal)
-                   sv_2mortal(dstr);
-               if (do_utf8)
-                   (void)SvUTF8_on(dstr);
+               dstr = newSVpvn_flags(s, m-s,
+                                     (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
@@ -4563,11 +4892,8 @@ PP(pp_split)
              (m = fbm_instr((unsigned char*)s, (unsigned char*)strend,
                             csv, multiline ? FBMrf_MULTILINE : 0)) )
            {
-               dstr = newSVpvn(s, m-s);
-               if (make_mortal)
-                   sv_2mortal(dstr);
-               if (do_utf8)
-                   (void)SvUTF8_on(dstr);
+               dstr = newSVpvn_flags(s, m-s,
+                                     (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
@@ -4579,53 +4905,48 @@ PP(pp_split)
        }
     }
     else {
-       maxiters += slen * rx->nparens;
+       maxiters += slen * RX_NPARENS(rx);
        while (s < strend && --limit)
        {
            I32 rex_return;
            PUTBACK;
-           rex_return = CALLREGEXEC(aTHX_ rx, (char*)s, (char*)strend, (char*)orig, 1 ,
+           rex_return = CALLREGEXEC(rx, (char*)s, (char*)strend, (char*)orig, 1 ,
                            sv, NULL, 0);
            SPAGAIN;
            if (rex_return == 0)
                break;
            TAINT_IF(RX_MATCH_TAINTED(rx));
-           if (RX_MATCH_COPIED(rx) && rx->subbeg != orig) {
+           if (RX_MATCH_COPIED(rx) && RX_SUBBEG(rx) != orig) {
                m = s;
                s = orig;
-               orig = rx->subbeg;
+               orig = RX_SUBBEG(rx);
                s = orig + (m - s);
                strend = s + (strend - m);
            }
-           m = rx->startp[0] + orig;
-           dstr = newSVpvn(s, m-s);
-           if (make_mortal)
-               sv_2mortal(dstr);
-           if (do_utf8)
-               (void)SvUTF8_on(dstr);
+           m = RX_OFFS(rx)[0].start + orig;
+           dstr = newSVpvn_flags(s, m-s,
+                                 (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
            XPUSHs(dstr);
-           if (rx->nparens) {
+           if (RX_NPARENS(rx)) {
                I32 i;
-               for (i = 1; i <= (I32)rx->nparens; i++) {
-                   s = rx->startp[i] + orig;
-                   m = rx->endp[i] + orig;
+               for (i = 1; i <= (I32)RX_NPARENS(rx); i++) {
+                   s = RX_OFFS(rx)[i].start + orig;
+                   m = RX_OFFS(rx)[i].end + orig;
 
                    /* japhy (07/27/01) -- the (m && s) test doesn't catch
                       parens that didn't match -- they should be set to
                       undef, not the empty string */
                    if (m >= orig && s >= orig) {
-                       dstr = newSVpvn(s, m-s);
+                       dstr = newSVpvn_flags(s, m-s,
+                                            (do_utf8 ? SVf_UTF8 : 0)
+                                             | make_mortal);
                    }
                    else
                        dstr = &PL_sv_undef;  /* undef, not "" */
-                   if (make_mortal)
-                       sv_2mortal(dstr);
-                   if (do_utf8)
-                       (void)SvUTF8_on(dstr);
                    XPUSHs(dstr);
                }
            }
-           s = rx->endp[0] + orig;
+           s = RX_OFFS(rx)[0].end + orig;
        }
     }
 
@@ -4636,11 +4957,7 @@ PP(pp_split)
     /* keep field after final delim? */
     if (s < strend || (iters && origlimit)) {
         const STRLEN l = strend - s;
-       dstr = newSVpvn(s, l);
-       if (make_mortal)
-           sv_2mortal(dstr);
-       if (do_utf8)
-           (void)SvUTF8_on(dstr);
+       dstr = newSVpvn_flags(s, l, (do_utf8 ? SVf_UTF8 : 0) | make_mortal);
        XPUSHs(dstr);
        iters++;
     }
@@ -4698,6 +5015,19 @@ PP(pp_split)
     RETURN;
 }
 
+PP(pp_once)
+{
+    dSP;
+    SV *const sv = PAD_SVl(PL_op->op_targ);
+
+    if (SvPADSTALE(sv)) {
+       /* First time. */
+       SvPADSTALE_off(sv);
+       RETURNOP(cLOGOP->op_other);
+    }
+    RETURNOP(cLOGOP->op_next);
+}
+
 PP(pp_lock)
 {
     dVAR;