This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Goodbye xav_arylen. You won't be missed that much.
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 8e50bec..97a5cfb 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, by Larry Wall and others
+ *    2000, 2001, 2002, 2003, 2004, 2005, 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.
  * and no knowing what you'll find around a corner.  And Elves, sir!" --Samwise
  */
 
+/* This file contains general pp ("push/pop") functions that execute the
+ * opcodes that make up a perl program. A typical pp function expects to
+ * find its arguments on the stack, and usually pushes its results onto
+ * the stack, hence the 'pp' terminology. Each OP structure contains
+ * a pointer to the relevant pp_foo() function.
+ */
+
 #include "EXTERN.h"
 #define PERL_IN_PP_C
 #include "perl.h"
 extern Pid_t getpid (void);
 #endif
 
+/*
+ * Some BSDs and Cygwin default to POSIX math instead of IEEE.
+ * This switches them over to IEEE.
+ */
+#if defined(LIBM_LIB_VERSION)
+    _LIB_VERSION_TYPE _LIB_VERSION = _IEEE_;
+#endif
+
 /* variations on pp_null */
 
 PP(pp_stub)
@@ -63,7 +78,7 @@ PP(pp_padav)
     }
     gimme = GIMME_V;
     if (gimme == G_ARRAY) {
-       I32 maxarg = AvFILL((AV*)TARG) + 1;
+       const I32 maxarg = AvFILL((AV*)TARG) + 1;
        EXTEND(SP, maxarg);
        if (SvMAGICAL(TARG)) {
            U32 i;
@@ -79,7 +94,7 @@ PP(pp_padav)
     }
     else if (gimme == G_SCALAR) {
        SV* sv = sv_newmortal();
-       I32 maxarg = AvFILL((AV*)TARG) + 1;
+       const I32 maxarg = AvFILL((AV*)TARG) + 1;
        sv_setiv(sv, maxarg);
        PUSHs(sv);
     }
@@ -140,9 +155,6 @@ PP(pp_rv2gv)
     }
     else {
        if (SvTYPE(sv) != SVt_PVGV) {
-           char *sym;
-           STRLEN len;
-
            if (SvGMAGICAL(sv)) {
                mg_get(sv);
                if (SvROK(sv))
@@ -152,23 +164,29 @@ PP(pp_rv2gv)
                /* If this is a 'my' scalar and flag is set then vivify
                 * NI-S 1999/05/07
                 */
+               if (SvREADONLY(sv))
+                   Perl_croak(aTHX_ PL_no_modify);
                if (PL_op->op_private & OPpDEREF) {
-                   char *name;
                    GV *gv;
                    if (cUNOP->op_targ) {
                        STRLEN len;
                        SV *namesv = PAD_SV(cUNOP->op_targ);
-                       name = SvPV(namesv, len);
+                       const char *name = SvPV(namesv, len);
                        gv = (GV*)NEWSV(0,0);
                        gv_init(gv, CopSTASH(PL_curcop), name, len, 0);
                    }
                    else {
-                       name = CopSTASHPV(PL_curcop);
+                       const char *name = CopSTASHPV(PL_curcop);
                        gv = newGVgen(name);
                    }
                    if (SvTYPE(sv) < SVt_RV)
                        sv_upgrade(sv, SVt_RV);
-                   SvRV(sv) = (SV*)gv;
+                   if (SvPVX_const(sv)) {
+                       SvPV_free(sv);
+                       SvLEN_set(sv, 0);
+                        SvCUR_set(sv, 0);
+                   }
+                   SvRV_set(sv, (SV*)gv);
                    SvROK_on(sv);
                    SvSETMAGIC(sv);
                    goto wasref;
@@ -177,25 +195,24 @@ PP(pp_rv2gv)
                    PL_op->op_private & HINT_STRICT_REFS)
                    DIE(aTHX_ PL_no_usym, "a symbol");
                if (ckWARN(WARN_UNINITIALIZED))
-                   report_uninit();
+                   report_uninit(sv);
                RETSETUNDEF;
            }
-           sym = SvPV(sv,len);
            if ((PL_op->op_flags & OPf_SPECIAL) &&
                !(PL_op->op_flags & OPf_MOD))
            {
-               sv = (SV*)gv_fetchpv(sym, FALSE, SVt_PVGV);
-               if (!sv
-                   && (!is_gv_magical(sym,len,0)
-                       || !(sv = (SV*)gv_fetchpv(sym, TRUE, SVt_PVGV))))
-               {
+               SV * temp = (SV*)gv_fetchsv(sv, FALSE, SVt_PVGV);
+               if (!temp
+                   && (!is_gv_magical_sv(sv,0)
+                       || !(sv = (SV*)gv_fetchsv(sv, TRUE, SVt_PVGV)))) {
                    RETSETUNDEF;
                }
+               sv = temp;
            }
            else {
                if (PL_op->op_private & HINT_STRICT_REFS)
-                   DIE(aTHX_ PL_no_symref, sym, "a symbol");
-               sv = (SV*)gv_fetchpv(sym, TRUE, SVt_PVGV);
+                   DIE(aTHX_ PL_no_symref_sv, sv, "a symbol");
+               sv = (SV*)gv_fetchsv(sv, TRUE, SVt_PVGV);
            }
        }
     }
@@ -223,8 +240,6 @@ PP(pp_rv2sv)
        }
     }
     else {
-       char *sym;
-       STRLEN len;
        gv = (GV*)sv;
 
        if (SvTYPE(gv) != SVt_PVGV) {
@@ -238,25 +253,24 @@ PP(pp_rv2sv)
                    PL_op->op_private & HINT_STRICT_REFS)
                    DIE(aTHX_ PL_no_usym, "a SCALAR");
                if (ckWARN(WARN_UNINITIALIZED))
-                   report_uninit();
+                   report_uninit(sv);
                RETSETUNDEF;
            }
-           sym = SvPV(sv, len);
            if ((PL_op->op_flags & OPf_SPECIAL) &&
                !(PL_op->op_flags & OPf_MOD))
            {
-               gv = (GV*)gv_fetchpv(sym, FALSE, SVt_PV);
+               gv = (GV*)gv_fetchsv(sv, FALSE, SVt_PV);
                if (!gv
-                   && (!is_gv_magical(sym,len,0)
-                       || !(gv = (GV*)gv_fetchpv(sym, TRUE, SVt_PV))))
+                   && (!is_gv_magical_sv(sv, 0)
+                       || !(gv = (GV*)gv_fetchsv(sv, TRUE, SVt_PV))))
                {
                    RETSETUNDEF;
                }
            }
            else {
                if (PL_op->op_private & HINT_STRICT_REFS)
-                   DIE(aTHX_ PL_no_symref, sym, "a SCALAR");
-               gv = (GV*)gv_fetchpv(sym, TRUE, SVt_PV);
+                   DIE(aTHX_ PL_no_symref_sv, sv, "a SCALAR");
+               gv = (GV*)gv_fetchsv(sv, TRUE, SVt_PV);
            }
        }
        sv = GvSV(gv);
@@ -281,13 +295,13 @@ PP(pp_av2arylen)
 {
     dSP;
     AV *av = (AV*)TOPs;
-    SV *sv = AvARYLEN(av);
-    if (!sv) {
-       AvARYLEN(av) = sv = NEWSV(0,0);
-       sv_upgrade(sv, SVt_IV);
-       sv_magic(sv, (SV*)av, PERL_MAGIC_arylen, Nullch, 0);
+    SV **sv = Perl_av_arylen_p(aTHX_ (AV*)av);
+    if (!*sv) {
+       *sv = NEWSV(0,0);
+       sv_upgrade(*sv, SVt_PVMG);
+       sv_magic(*sv, (SV*)av, PERL_MAGIC_arylen, Nullch, 0);
     }
-    SETs(sv);
+    SETs(*sv);
     RETURN;
 }
 
@@ -362,18 +376,17 @@ PP(pp_prototype)
 
     ret = &PL_sv_undef;
     if (SvPOK(TOPs) && SvCUR(TOPs) >= 7) {
-       char *s = SvPVX(TOPs);
+       const char *s = SvPVX_const(TOPs);
        if (strnEQ(s, "CORE::", 6)) {
-           int code;
-       
-           code = keyword(s + 6, SvCUR(TOPs) - 6);
+           const int code = keyword(s + 6, SvCUR(TOPs) - 6);
            if (code < 0) {     /* Overridable. */
 #define MAX_ARGS_OP ((sizeof(I32) - 1) * 2)
                int i = 0, n = 0, seen_question = 0;
                I32 oa;
                char str[ MAX_ARGS_OP * 2 + 2 ]; /* One ';', one '\0' */
 
-               if (code == -KEY_chop || code == -KEY_chomp)
+               if (code == -KEY_chop || code == -KEY_chomp
+                       || code == -KEY_exec || code == -KEY_system)
                    goto set;
                while (i < MAXO) {      /* The slow way. */
                    if (strEQ(s + 6, PL_op_name[i])
@@ -391,8 +404,6 @@ PP(pp_prototype)
                        seen_question = 1;
                        str[n++] = ';';
                    }
-                   else if (n && str[0] == ';' && seen_question)
-                       goto set;       /* XXXX system, exec */
                    if ((oa & (OA_OPTIONAL - 1)) >= OA_AVREF
                        && (oa & (OA_OPTIONAL - 1)) <= OA_SCALARREF
                        /* But globs are already references (kinda) */
@@ -416,7 +427,7 @@ PP(pp_prototype)
     }
     cv = sv_2cv(TOPs, &stash, &gv, FALSE);
     if (cv && SvPOK(cv))
-       ret = sv_2mortal(newSVpvn(SvPVX(cv), SvCUR(cv)));
+       ret = sv_2mortal(newSVpvn(SvPVX_const(cv), SvCUR(cv)));
   set:
     SETs(ret);
     RETURN;
@@ -485,7 +496,7 @@ S_refto(pTHX_ SV *sv)
     }
     rv = sv_newmortal();
     sv_upgrade(rv, SVt_RV);
-    SvRV(rv) = sv;
+    SvRV_set(rv, sv);
     SvROK_on(rv);
     return rv;
 }
@@ -494,7 +505,7 @@ PP(pp_ref)
 {
     dSP; dTARGET;
     SV *sv;
-    char *pv;
+    const char *pv;
 
     sv = POPs;
 
@@ -520,7 +531,7 @@ PP(pp_bless)
     else {
        SV *ssv = POPs;
        STRLEN len;
-       char *ptr;
+       const char *ptr;
 
        if (ssv && !SvGMAGICAL(ssv) && !SvAMAGIC(ssv) && SvROK(ssv))
            Perl_croak(aTHX_ "Attempt to bless into a reference");
@@ -540,7 +551,7 @@ PP(pp_gelem)
     GV *gv;
     SV *sv;
     SV *tmpRef;
-    char *elem;
+    const char *elem;
     dSP;
     STRLEN n_a;
 
@@ -549,54 +560,56 @@ PP(pp_gelem)
     gv = (GV*)POPs;
     tmpRef = Nullsv;
     sv = Nullsv;
-    switch (elem ? *elem : '\0')
-    {
-    case 'A':
-       if (strEQ(elem, "ARRAY"))
-           tmpRef = (SV*)GvAV(gv);
-       break;
-    case 'C':
-       if (strEQ(elem, "CODE"))
-           tmpRef = (SV*)GvCVu(gv);
-       break;
-    case 'F':
-       if (strEQ(elem, "FILEHANDLE")) {
-           /* finally deprecated in 5.8.0 */
-           deprecate("*glob{FILEHANDLE}");
-           tmpRef = (SV*)GvIOp(gv);
+    if (elem) {
+       /* elem will always be NUL terminated.  */
+       const char *elem2 = elem + 1;
+       switch (*elem) {
+       case 'A':
+           if (strEQ(elem2, "RRAY"))
+               tmpRef = (SV*)GvAV(gv);
+           break;
+       case 'C':
+           if (strEQ(elem2, "ODE"))
+               tmpRef = (SV*)GvCVu(gv);
+           break;
+       case 'F':
+           if (strEQ(elem2, "ILEHANDLE")) {
+               /* finally deprecated in 5.8.0 */
+               deprecate("*glob{FILEHANDLE}");
+               tmpRef = (SV*)GvIOp(gv);
+           }
+           else
+               if (strEQ(elem2, "ORMAT"))
+                   tmpRef = (SV*)GvFORM(gv);
+           break;
+       case 'G':
+           if (strEQ(elem2, "LOB"))
+               tmpRef = (SV*)gv;
+           break;
+       case 'H':
+           if (strEQ(elem2, "ASH"))
+               tmpRef = (SV*)GvHV(gv);
+           break;
+       case 'I':
+           if (*elem2 == 'O' && !elem[2])
+               tmpRef = (SV*)GvIOp(gv);
+           break;
+       case 'N':
+           if (strEQ(elem2, "AME"))
+               sv = newSVpvn(GvNAME(gv), GvNAMELEN(gv));
+           break;
+       case 'P':
+           if (strEQ(elem2, "ACKAGE")) {
+               const char *name = HvNAME_get(GvSTASH(gv));
+               sv = newSVpvn(name ? name : "__ANON__",
+                             name ? HvNAMELEN_get(GvSTASH(gv)) : 8);
+           }
+           break;
+       case 'S':
+           if (strEQ(elem2, "CALAR"))
+               tmpRef = GvSV(gv);
+           break;
        }
-       else
-       if (strEQ(elem, "FORMAT"))
-           tmpRef = (SV*)GvFORM(gv);
-       break;
-    case 'G':
-       if (strEQ(elem, "GLOB"))
-           tmpRef = (SV*)gv;
-       break;
-    case 'H':
-       if (strEQ(elem, "HASH"))
-           tmpRef = (SV*)GvHV(gv);
-       break;
-    case 'I':
-       if (strEQ(elem, "IO"))
-           tmpRef = (SV*)GvIOp(gv);
-       break;
-    case 'N':
-       if (strEQ(elem, "NAME"))
-           sv = newSVpvn(GvNAME(gv), GvNAMELEN(gv));
-       break;
-    case 'P':
-        if (strEQ(elem, "PACKAGE")) {
-            if (HvNAME(GvSTASH(gv)))
-                sv = newSVpv(HvNAME(GvSTASH(gv)), 0);
-            else
-                sv = newSVpv("__ANON__",0);
-        }
-       break;
-    case 'S':
-       if (strEQ(elem, "SCALAR"))
-           tmpRef = GvSV(gv);
-       break;
     }
     if (tmpRef)
        sv = newRV(tmpRef);
@@ -814,13 +827,12 @@ PP(pp_undef)
        }
        break;
     default:
-       if (SvTYPE(sv) >= SVt_PV && SvPVX(sv) && SvLEN(sv)) {
-           (void)SvOOK_off(sv);
-           Safefree(SvPVX(sv));
+       if (SvTYPE(sv) >= SVt_PV && SvPVX_const(sv) && SvLEN(sv)) {
+           SvPV_free(sv);
            SvPV_set(sv, Nullch);
            SvLEN_set(sv, 0);
        }
-       (void)SvOK_off(sv);
+       SvOK_off(sv);
        SvSETMAGIC(sv);
     }
 
@@ -830,12 +842,12 @@ PP(pp_undef)
 PP(pp_predec)
 {
     dSP;
-    if (SvTYPE(TOPs) >= SVt_PVGV && ! SvTYPE(TOPs) == SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
         && SvIVX(TOPs) != IV_MIN)
     {
-       --SvIVX(TOPs);
+       SvIV_set(TOPs, SvIVX(TOPs) - 1);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
     }
     else
@@ -847,13 +859,13 @@ PP(pp_predec)
 PP(pp_postinc)
 {
     dSP; dTARGET;
-    if (SvTYPE(TOPs) >= SVt_PVGV && ! SvTYPE(TOPs) == SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
         && SvIVX(TOPs) != IV_MAX)
     {
-       ++SvIVX(TOPs);
+       SvIV_set(TOPs, SvIVX(TOPs) + 1);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
     }
     else
@@ -869,13 +881,13 @@ PP(pp_postinc)
 PP(pp_postdec)
 {
     dSP; dTARGET;
-    if (SvTYPE(TOPs) >= SVt_PVGV && ! SvTYPE(TOPs) == SVt_PVLV)
+    if (SvTYPE(TOPs) >= SVt_PVGV && SvTYPE(TOPs) != SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
         && SvIVX(TOPs) != IV_MIN)
     {
-       --SvIVX(TOPs);
+       SvIV_set(TOPs, SvIVX(TOPs) - 1);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
     }
     else
@@ -1385,13 +1397,46 @@ PP(pp_repeat)
 {
   dSP; dATARGET; tryAMAGICbin(repeat,opASSIGN);
   {
-    register IV count = POPi;
+    register IV count;
+    dPOPss;
+    if (SvGMAGICAL(sv))
+        mg_get(sv);
+    if (SvIOKp(sv)) {
+        if (SvUOK(sv)) {
+             UV uv = SvUV(sv);
+             if (uv > IV_MAX)
+                  count = IV_MAX; /* The best we can do? */
+             else
+                  count = uv;
+        } else {
+             IV iv = SvIV(sv);
+             if (iv < 0)
+                  count = 0;
+             else
+                  count = iv;
+        }
+    }
+    else if (SvNOKp(sv)) {
+        NV nv = SvNV(sv);
+        if (nv < 0.0)
+             count = 0;
+        else
+             count = (IV)nv;
+    }
+    else
+        count = SvIVx(sv);
     if (GIMME == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
        dMARK;
        I32 items = SP - MARK;
        I32 max;
+       static const char oom_list_extend[] =
+         "Out of memory during list extend";
 
        max = items * count;
+       MEM_WRAP_CHECK_1(max, SV*, oom_list_extend);
+       /* Did the max computation overflow? */
+       if (items > 0 && max > 0 && (max < items || max < count))
+          Perl_croak(aTHX_ oom_list_extend);
        MEXTEND(MARK, max);
        if (count > 1) {
            while (SP > MARK) {
@@ -1436,6 +1481,8 @@ PP(pp_repeat)
        SV *tmpstr = POPs;
        STRLEN len;
        bool isutf;
+       static const char oom_string_extend[] =
+         "Out of memory during string extend";
 
        SvSetSV(TARG, tmpstr);
        SvPV_force(TARG, len);
@@ -1444,9 +1491,13 @@ PP(pp_repeat)
            if (count < 1)
                SvCUR_set(TARG, 0);
            else {
-               SvGROW(TARG, (count * len) + 1);
+               STRLEN max = (UV)count * len;
+               if (len > ((MEM_SIZE)~0)/count)
+                    Perl_croak(aTHX_ oom_string_extend);
+               MEM_WRAP_CHECK_1(max, char, oom_string_extend);
+               SvGROW(TARG, max + 1);
                repeatcpy(SvPVX(TARG) + len, SvPVX(TARG), len, count - 1);
-               SvCUR(TARG) *= count;
+               SvCUR_set(TARG, SvCUR(TARG) * count);
            }
            *SvEND(TARG) = '\0';
        }
@@ -1686,11 +1737,11 @@ PP(pp_lt)
 #ifdef PERL_PRESERVE_IVUV
     else
 #endif
-        if (SvROK(TOPs) && SvROK(TOPm1s)) {
-            SP--;
-            SETs(boolSV(SvRV(TOPs) < SvRV(TOPp1s)));
-            RETURN;
-        }
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
+       SP--;
+       SETs(boolSV(SvRV(TOPs) < SvRV(TOPp1s)));
+       RETURN;
+    }
 #endif
     {
       dPOPnv;
@@ -1764,7 +1815,7 @@ PP(pp_gt)
 #ifdef PERL_PRESERVE_IVUV
     else
 #endif
-        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
         SP--;
         SETs(boolSV(SvRV(TOPs) > SvRV(TOPp1s)));
         RETURN;
@@ -1842,7 +1893,7 @@ PP(pp_le)
 #ifdef PERL_PRESERVE_IVUV
     else
 #endif
-        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
         SP--;
         SETs(boolSV(SvRV(TOPs) <= SvRV(TOPp1s)));
         RETURN;
@@ -1920,7 +1971,7 @@ PP(pp_ge)
 #ifdef PERL_PRESERVE_IVUV
     else
 #endif
-        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
         SP--;
         SETs(boolSV(SvRV(TOPs) >= SvRV(TOPp1s)));
         RETURN;
@@ -1937,7 +1988,7 @@ PP(pp_ne)
 {
     dSP; tryAMAGICbinSET(ne,0);
 #ifndef NV_PRESERVES_UV
-    if (SvROK(TOPs) && SvROK(TOPm1s)) {
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
         SP--;
        SETs(boolSV(SvRV(TOPs) != SvRV(TOPp1s)));
        RETURN;
@@ -2006,7 +2057,7 @@ PP(pp_ncmp)
 {
     dSP; dTARGET; tryAMAGICbin(ncmp,0);
 #ifndef NV_PRESERVES_UV
-    if (SvROK(TOPs) && SvROK(TOPm1s)) {
+    if (SvROK(TOPs) && !SvAMAGIC(TOPs) && SvROK(TOPm1s) && !SvAMAGIC(TOPm1s)) {
         UV right = PTR2UV(SvRV(POPs));
         UV left = PTR2UV(SvRV(TOPs));
        SETi((left > right) - (left < right));
@@ -2372,6 +2423,7 @@ PP(pp_complement)
        register I32 anum;
        STRLEN len;
 
+       (void)SvPV_nomg(sv,len); /* force check for uninit var */
        sv_setsv_nomg(TARG, sv);
        tmps = (U8*)SvPV_force(TARG, len);
        anum = len;
@@ -2474,7 +2526,7 @@ STATIC
 PP(pp_i_modulo_0)
 {
      /* This is the vanilla old i_modulo. */
-     dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+     dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
      {
          dPOPTOPiirl;
          if (!right)
@@ -2491,7 +2543,7 @@ 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. */
-     dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+     dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
      {
          dPOPTOPiirl;
          if (!right)
@@ -2504,7 +2556,7 @@ PP(pp_i_modulo_1)
 
 PP(pp_i_modulo)
 {
-     dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+     dVAR; dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
      {
          dPOPTOPiirl;
          if (!right)
@@ -2786,7 +2838,9 @@ PP(pp_int)
         else preferring IV has introduced a subtle behaviour change bug. OTOH
         relying on floating point to be accurate is a bug.  */
 
-      if (SvIOK(TOPs)) {
+      if (!SvOK(TOPs))
+        SETu(0);
+      else if (SvIOK(TOPs)) {
        if (SvIsUV(TOPs)) {
            UV uv = TOPu;
            SETu(uv);
@@ -2820,7 +2874,9 @@ PP(pp_abs)
       /* This will cache the NV value if string isn't actually integer  */
       IV iv = TOPi;
 
-      if (SvIOK(TOPs)) {
+      if (!SvOK(TOPs))
+        SETu(0);
+      else if (SvIOK(TOPs)) {
        /* IVX is precise  */
        if (SvIsUV(TOPs)) {
          SETu(TOPu);   /* force it to be numeric only */
@@ -2942,11 +2998,11 @@ PP(pp_substr)
     I32 pos;
     I32 rem;
     I32 fail;
-    I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
-    char *tmps;
-    I32 arybase = PL_curcop->cop_arybase;
+    const I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
+    const char *tmps;
+    const I32 arybase = PL_curcop->cop_arybase;
     SV *repl_sv = NULL;
-    char *repl = 0;
+    const char *repl = 0;
     STRLEN repl_len;
     int num_args = PL_op->op_private & 7;
     bool repl_need_utf8_upgrade = FALSE;
@@ -3030,6 +3086,19 @@ PP(pp_substr)
        if (utf8_curlen)
            sv_pos_u2b(sv, &pos, &rem);
        tmps += pos;
+       /* we either return a PV or an LV. If the TARG hasn't been used
+        * before, or is of that type, reuse it; otherwise use a mortal
+        * instead. Note that LVs can have an extended lifetime, so also
+        * dont reuse if refcount > 1 (bug #20933) */
+       if (SvTYPE(TARG) > SVt_NULL) {
+           if ( (SvTYPE(TARG) == SVt_PVLV)
+                   ? (!lvalue || SvREFCNT(TARG) > 1)
+                   : lvalue)
+           {
+               TARG = sv_newmortal();
+           }
+       }
+
        sv_setpvn(TARG, tmps, rem);
 #ifdef USE_LOCALE_COLLATE
        sv_unmagic(TARG, PERL_MAGIC_collxfrm);
@@ -3066,14 +3135,12 @@ PP(pp_substr)
                    sv_setpvn(sv,"",0); /* avoid lexical reincarnation */
            }
 
-           if (SvREFCNT(TARG) > 1)     /* don't share the TARG (#20933) */
-               TARG = sv_newmortal();
            if (SvTYPE(TARG) < SVt_PVLV) {
                sv_upgrade(TARG, SVt_PVLV);
                sv_magic(TARG, Nullsv, PERL_MAGIC_substr, Nullch, 0);
            }
            else
-               (void)SvOK_off(TARG);
+               SvOK_off(TARG);
 
            LvTYPE(TARG) = 'x';
            if (LvTARG(TARG) != sv) {
@@ -3126,12 +3193,15 @@ PP(pp_index)
     dSP; dTARGET;
     SV *big;
     SV *little;
+    SV *temp = Nullsv;
     I32 offset;
     I32 retval;
     char *tmps;
     char *tmps2;
     STRLEN biglen;
     I32 arybase = PL_curcop->cop_arybase;
+    int big_utf8;
+    int little_utf8;
 
     if (MAXARG < 3)
        offset = 0;
@@ -3139,9 +3209,31 @@ PP(pp_index)
        offset = POPi - arybase;
     little = POPs;
     big = POPs;
-    tmps = SvPV(big, biglen);
-    if (offset > 0 && DO_UTF8(big))
+    big_utf8 = DO_UTF8(big);
+    little_utf8 = DO_UTF8(little);
+    if (big_utf8 ^ little_utf8) {
+       /* One needs to be upgraded.  */
+       SV *bytes = little_utf8 ? big : little;
+       STRLEN len;
+       char *p = SvPV(bytes, len);
+
+       temp = newSVpvn(p, len);
+
+       if (PL_encoding) {
+           sv_recode_to_utf8(temp, PL_encoding);
+       } else {
+           sv_utf8_upgrade(temp);
+       }
+       if (little_utf8) {
+           big = temp;
+           big_utf8 = TRUE;
+       } else {
+           little = temp;
+       }
+    }
+    if (big_utf8 && offset > 0)
        sv_pos_u2b(big, &offset, 0);
+    tmps = SvPV(big, biglen);
     if (offset < 0)
        offset = 0;
     else if (offset > (I32)biglen)
@@ -3151,8 +3243,10 @@ PP(pp_index)
        retval = -1;
     else
        retval = tmps2 - tmps;
-    if (retval > 0 && DO_UTF8(big))
+    if (retval > 0 && big_utf8)
        sv_pos_b2u(big, &retval);
+    if (temp)
+       SvREFCNT_dec(temp);
     PUSHi(retval + arybase);
     RETURN;
 }
@@ -3162,6 +3256,7 @@ PP(pp_rindex)
     dSP; dTARGET;
     SV *big;
     SV *little;
+    SV *temp = Nullsv;
     STRLEN blen;
     STRLEN llen;
     I32 offset;
@@ -3169,17 +3264,42 @@ PP(pp_rindex)
     char *tmps;
     char *tmps2;
     I32 arybase = PL_curcop->cop_arybase;
+    int big_utf8;
+    int little_utf8;
 
     if (MAXARG >= 3)
        offset = POPi;
     little = POPs;
     big = POPs;
+    big_utf8 = DO_UTF8(big);
+    little_utf8 = DO_UTF8(little);
+    if (big_utf8 ^ little_utf8) {
+       /* One needs to be upgraded.  */
+       SV *bytes = little_utf8 ? big : little;
+       STRLEN len;
+       char *p = SvPV(bytes, len);
+
+       temp = newSVpvn(p, len);
+
+       if (PL_encoding) {
+           sv_recode_to_utf8(temp, PL_encoding);
+       } else {
+           sv_utf8_upgrade(temp);
+       }
+       if (little_utf8) {
+           big = temp;
+           big_utf8 = TRUE;
+       } else {
+           little = temp;
+       }
+    }
     tmps2 = SvPV(little, llen);
     tmps = SvPV(big, blen);
+
     if (MAXARG < 3)
        offset = blen;
     else {
-       if (offset > 0 && DO_UTF8(big))
+       if (offset > 0 && big_utf8)
            sv_pos_u2b(big, &offset, 0);
        offset = offset - arybase + llen;
     }
@@ -3192,8 +3312,10 @@ PP(pp_rindex)
        retval = -1;
     else
        retval = tmps2 - tmps;
-    if (retval > 0 && DO_UTF8(big))
+    if (retval > 0 && big_utf8)
        sv_pos_b2u(big, &retval);
+    if (temp)
+       SvREFCNT_dec(temp);
     PUSHi(retval + arybase);
     RETURN;
 }
@@ -3225,7 +3347,7 @@ PP(pp_ord)
     }
 
     XPUSHu(DO_UTF8(argsv) ?
-          utf8n_to_uvchr(s, UTF8_MAXLEN, 0, UTF8_ALLOW_ANYUV) :
+          utf8n_to_uvchr(s, UTF8_MAXBYTES, 0, UTF8_ALLOW_ANYUV) :
           (*s & 0xff));
 
     RETURN;
@@ -3276,8 +3398,8 @@ PP(pp_chr)
 
 PP(pp_crypt)
 {
-    dSP; dTARGET;
 #ifdef HAS_CRYPT
+    dSP; dTARGET;
     dPOPTOPssrl;
     STRLEN n_a;
     STRLEN len;
@@ -3335,7 +3457,7 @@ PP(pp_ucfirst)
     if (DO_UTF8(sv) &&
        (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
        UTF8_IS_START(*s)) {
-       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
+       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
        STRLEN ulen;
        STRLEN tculen;
 
@@ -3398,7 +3520,7 @@ PP(pp_lcfirst)
        (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
        UTF8_IS_START(*s)) {
        STRLEN ulen;
-       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
+       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
        U8 *tend;
        UV uv;
 
@@ -3455,7 +3577,7 @@ PP(pp_uc)
        STRLEN ulen;
        register U8 *d;
        U8 *send;
-       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
+       U8 tmpbuf[UTF8_MAXBYTES+1];
 
        s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
@@ -3464,18 +3586,32 @@ PP(pp_uc)
            SETs(TARG);
        }
        else {
-           STRLEN nchar = utf8_length(s, s + len);
+           STRLEN min = len + 1;
 
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
+           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. */
+                   UV o = d - (U8*)SvPVX(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 += UTF8SKIP(s);
+               s += u;
            }
            *d = '\0';
            SvUTF8_on(TARG);
@@ -3524,7 +3660,7 @@ PP(pp_lc)
        STRLEN ulen;
        register U8 *d;
        U8 *send;
-       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
+       U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
 
        s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
@@ -3533,16 +3669,18 @@ PP(pp_lc)
            SETs(TARG);
        }
        else {
-           STRLEN nchar = utf8_length(s, s + len);
+           STRLEN min = len + 1;
 
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
+           SvGROW(TARG, min);
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
            while (s < send) {
+               STRLEN u = UTF8SKIP(s);
                UV uv = toLOWER_utf8(s, tmpbuf, &ulen);
-#define GREEK_CAPITAL_LETTER_SIGMA 0x03A3 /* Unicode */
+
+#define GREEK_CAPITAL_LETTER_SIGMA 0x03A3 /* Unicode U+03A3 */
                if (uv == GREEK_CAPITAL_LETTER_SIGMA) {
                     /*
                      * Now if the sigma is NOT followed by
@@ -3556,12 +3694,26 @@ PP(pp_lc)
                      * then it should be mapped to 0x03C2,
                      * (GREEK SMALL LETTER FINAL SIGMA),
                      * instead of staying 0x03A3.
-                     * See lib/unicore/SpecCase.txt.
+                     * "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. */
+                   UV o = d - (U8*)SvPVX(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 += UTF8SKIP(s);
+               s += u;
            }
            *d = '\0';
            SvUTF8_on(TARG);
@@ -3688,7 +3840,7 @@ PP(pp_aslice)
     }
     if (GIMME != G_ARRAY) {
        MARK = ORIGMARK;
-       *++MARK = *SP;
+       *++MARK = SP > ORIGMARK ? *SP : &PL_sv_undef;
        SP = MARK;
     }
     RETURN;
@@ -3701,7 +3853,7 @@ PP(pp_each)
     dSP;
     HV *hash = (HV*)POPs;
     HE *entry;
-    I32 gimme = GIMME_V;
+    const I32 gimme = GIMME_V;
 
     PUTBACK;
     /* might clobber stack_sp */
@@ -3740,8 +3892,8 @@ PP(pp_keys)
 PP(pp_delete)
 {
     dSP;
-    I32 gimme = GIMME_V;
-    I32 discard = (gimme == G_VOID) ? G_DISCARD : 0;
+    const I32 gimme = GIMME_V;
+    const I32 discard = (gimme == G_VOID) ? G_DISCARD : 0;
     SV *sv;
     HV *hv;
 
@@ -3770,7 +3922,10 @@ PP(pp_delete)
            SP = ORIGMARK;
        else if (gimme == G_SCALAR) {
            MARK = ORIGMARK;
-           *++MARK = *SP;
+           if (SP > MARK)
+               *++MARK = *SP;
+           else
+               *++MARK = &PL_sv_undef;
            SP = MARK;
        }
     }
@@ -3868,8 +4023,7 @@ PP(pp_hslice)
 
         if (lval) {
             if (!svp || *svp == &PL_sv_undef) {
-                STRLEN n_a;
-                DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
+                DIE(aTHX_ PL_no_helem_sv, keysv);
             }
             if (localizing) {
                 if (preeminent)
@@ -3885,7 +4039,7 @@ PP(pp_hslice)
     }
     if (GIMME != G_ARRAY) {
        MARK = ORIGMARK;
-       *++MARK = *SP;
+       *++MARK = SP > ORIGMARK ? *SP : &PL_sv_undef;
        SP = MARK;
     }
     RETURN;
@@ -3992,7 +4146,7 @@ PP(pp_anonhash)
 
 PP(pp_splice)
 {
-    dSP; dMARK; dORIGMARK;
+    dVAR; dSP; dMARK; dORIGMARK;
     register AV *ary = (AV*)*++MARK;
     register SV **src;
     register SV **dst;
@@ -4061,6 +4215,12 @@ PP(pp_splice)
     if (newlen && !AvREAL(ary) && AvREIFY(ary))
        av_reify(ary);
 
+    /* make new elements SVs now: avoid problems if they're from the array */
+    for (dst = MARK, i = newlen; i; i--) {
+        SV *h = *dst;
+       *dst++ = newSVsv(h);
+    }
+
     if (diff < 0) {                            /* shrinking the area */
        if (newlen) {
            New(451, tmparyval, newlen, SV*);   /* so remember insertion */
@@ -4100,7 +4260,7 @@ PP(pp_splice)
                    *dst-- = *src--;
            }
            dst = AvARRAY(ary);
-           SvPVX(ary) = (char*)(AvARRAY(ary) - diff); /* diff is negative */
+           SvPV_set(ary, (char*)(AvARRAY(ary) - diff)); /* diff is negative */
            AvMAX(ary) += diff;
        }
        else {
@@ -4117,11 +4277,7 @@ PP(pp_splice)
            dst[--i] = &PL_sv_undef;
        
        if (newlen) {
-           for (src = tmparyval, dst = AvARRAY(ary) + offset;
-             newlen; newlen--) {
-               *dst = NEWSV(46, 0);
-               sv_setsv(*dst++, *src++);
-           }
+           Copy( tmparyval, AvARRAY(ary) + offset, newlen, SV* );
            Safefree(tmparyval);
        }
     }
@@ -4141,7 +4297,7 @@ PP(pp_splice)
                    dst = src - diff;
                    Move(src, dst, offset, SV*);
                }
-               SvPVX(ary) = (char*)(AvARRAY(ary) - diff);/* diff is positive */
+               SvPV_set(ary, (char*)(AvARRAY(ary) - diff));/* diff is positive */
                AvMAX(ary) += diff;
                AvFILLp(ary) += diff;
            }
@@ -4160,10 +4316,10 @@ PP(pp_splice)
            }
        }
 
-       for (src = MARK, dst = AvARRAY(ary) + offset; newlen; newlen--) {
-           *dst = NEWSV(46, 0);
-           sv_setsv(*dst++, *src++);
+       if (newlen) {
+           Copy( MARK, AvARRAY(ary) + offset, newlen, SV* );
        }
+
        MARK = ORIGMARK + 1;
        if (GIMME == G_ARRAY) {                 /* copy return vals to stack */
            if (length) {
@@ -4197,7 +4353,7 @@ PP(pp_splice)
 
 PP(pp_push)
 {
-    dSP; dMARK; dORIGMARK; dTARGET;
+    dVAR; dSP; dMARK; dORIGMARK; dTARGET;
     register AV *ary = (AV*)*++MARK;
     register SV *sv = &PL_sv_undef;
     MAGIC *mg;
@@ -4252,7 +4408,7 @@ PP(pp_shift)
 
 PP(pp_unshift)
 {
-    dSP; dMARK; dORIGMARK; dTARGET;
+    dVAR; dSP; dMARK; dORIGMARK; dTARGET;
     register AV *ary = (AV*)*++MARK;
     register SV *sv;
     register I32 i = 0;
@@ -4270,8 +4426,7 @@ PP(pp_unshift)
     else {
        av_unshift(ary, SP - MARK);
        while (MARK < SP) {
-           sv = NEWSV(27, 0);
-           sv_setsv(sv, *++MARK);
+           sv = newSVsv(*++MARK);
            (void)av_store(ary, i++, sv);
        }
     }
@@ -4302,12 +4457,17 @@ PP(pp_reverse)
        register I32 tmp;
        dTARGET;
        STRLEN len;
+       I32 padoff_du;
 
        SvUTF8_off(TARG);                               /* decontaminate */
        if (SP - MARK > 1)
            do_join(TARG, &PL_sv_no, MARK, SP);
        else
-           sv_setsv(TARG, (SP > MARK) ? *SP : DEFSV);
+           sv_setsv(TARG, (SP > MARK)
+                   ? *SP
+                   : (padoff_du = find_rundefsvoffset(),
+                       (padoff_du == NOT_IN_PAD || PAD_COMPNAME_FLAGS(padoff_du) & SVpad_OUR)
+                       ? DEFSV : PAD_SVl(padoff_du)));
        up = SvPV_force(TARG, len);
        if (len > 1) {
            if (DO_UTF8(TARG)) {        /* first reverse each character */
@@ -4350,7 +4510,7 @@ PP(pp_reverse)
 
 PP(pp_split)
 {
-    dSP; dTARG;
+    dVAR; dSP; dTARG;
     AV *ary;
     register IV limit = POPi;                  /* note, negative is forever */
     SV *sv = POPs;
@@ -4363,17 +4523,17 @@ PP(pp_split)
     register SV *dstr;
     register char *m;
     I32 iters = 0;
-    STRLEN slen = do_utf8 ? utf8_length((U8*)s, (U8*)strend) : (strend - s);
+    const STRLEN slen = do_utf8 ? utf8_length((U8*)s, (U8*)strend) : (strend - s);
     I32 maxiters = slen + 10;
     I32 i;
     char *orig;
     I32 origlimit = limit;
     I32 realarray = 0;
     I32 base;
-    AV *oldstack = PL_curstack;
-    I32 gimme = GIMME_V;
-    I32 oldsave = PL_savestack_ix;
+    const I32 gimme = GIMME_V;
+    const I32 oldsave = PL_savestack_ix;
     I32 make_mortal = 1;
+    bool multiline = 0;
     MAGIC *mg = (MAGIC *) NULL;
 
 #ifdef DEBUGGING
@@ -4419,8 +4579,7 @@ PP(pp_split)
                    AvARRAY(ary)[i] = &PL_sv_undef;     /* don't free mere refs */
            }
            /* temporarily switch stacks */
-           SWITCHSTACK(PL_curstack, ary);
-           PL_curstackinfo->si_stack = ary;
+           SAVESWITCHSTACK(PL_curstack, ary);
            make_mortal = 0;
        }
     }
@@ -4436,9 +4595,8 @@ PP(pp_split)
                s++;
        }
     }
-    if ((int)(pm->op_pmflags & PMf_MULTILINE) != PL_multiline) {
-       SAVEINT(PL_multiline);
-       PL_multiline = pm->op_pmflags & PMf_MULTILINE;
+    if (pm->op_pmflags & PMf_MULTILINE) {
+       multiline = 1;
     }
 
     if (!limit)
@@ -4453,8 +4611,7 @@ PP(pp_split)
            if (m >= strend)
                break;
 
-           dstr = NEWSV(30, m-s);
-           sv_setpvn(dstr, s, m-s);
+           dstr = newSVpvn(s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            if (do_utf8)
@@ -4468,15 +4625,14 @@ PP(pp_split)
                ++s;
        }
     }
-    else if (strEQ("^", rx->precomp)) {
+    else if (rx->precomp[0] == '^' && rx->precomp[1] == '\0') {
        while (--limit) {
            /*SUPPRESS 530*/
            for (m = s; m < strend && *m != '\n'; m++) ;
            m++;
            if (m >= strend)
                break;
-           dstr = NEWSV(30, m-s);
-           sv_setpvn(dstr, s, m-s);
+           dstr = newSVpvn(s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            if (do_utf8)
@@ -4501,8 +4657,7 @@ PP(pp_split)
                for (m = s; m < strend && *m != c; m++) ;
                if (m >= strend)
                    break;
-               dstr = NEWSV(30, m-s);
-               sv_setpvn(dstr, s, m-s);
+               dstr = newSVpvn(s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
                if (do_utf8)
@@ -4517,14 +4672,11 @@ PP(pp_split)
            }
        }
        else {
-#ifndef lint
            while (s < strend && --limit &&
              (m = fbm_instr((unsigned char*)s, (unsigned char*)strend,
-                            csv, PL_multiline ? FBMrf_MULTILINE : 0)) )
-#endif
+                            csv, multiline ? FBMrf_MULTILINE : 0)) )
            {
-               dstr = NEWSV(31, m-s);
-               sv_setpvn(dstr, s, m-s);
+               dstr = newSVpvn(s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
                if (do_utf8)
@@ -4557,8 +4709,7 @@ PP(pp_split)
                strend = s + (strend - m);
            }
            m = rx->startp[0] + orig;
-           dstr = NEWSV(32, m-s);
-           sv_setpvn(dstr, s, m-s);
+           dstr = newSVpvn(s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            if (do_utf8)
@@ -4573,8 +4724,7 @@ PP(pp_split)
                       parens that didn't match -- they should be set to
                       undef, not the empty string */
                    if (m >= orig && s >= orig) {
-                       dstr = NEWSV(33, m-s);
-                       sv_setpvn(dstr, s, m-s);
+                       dstr = newSVpvn(s, m-s);
                    }
                    else
                        dstr = &PL_sv_undef;  /* undef, not "" */
@@ -4589,7 +4739,6 @@ PP(pp_split)
        }
     }
 
-    LEAVE_SCOPE(oldsave);
     iters = (SP - PL_stack_base) - base;
     if (iters > maxiters)
        DIE(aTHX_ "Split loop");
@@ -4597,8 +4746,7 @@ PP(pp_split)
     /* keep field after final delim? */
     if (s < strend || (iters && origlimit)) {
         STRLEN l = strend - s;
-       dstr = NEWSV(34, l);
-       sv_setpvn(dstr, s, l);
+       dstr = newSVpvn(s, l);
        if (make_mortal)
            sv_2mortal(dstr);
        if (do_utf8)
@@ -4611,14 +4759,15 @@ PP(pp_split)
            if (TOPs && !make_mortal)
                sv_2mortal(TOPs);
            iters--;
-           SP--;
+           *SP-- = &PL_sv_undef;
        }
     }
 
+    PUTBACK;
+    LEAVE_SCOPE(oldsave); /* may undo an earlier SWITCHSTACK */
+    SPAGAIN;
     if (realarray) {
        if (!mg) {
-           SWITCHSTACK(ary, oldstack);
-           PL_curstackinfo->si_stack = oldstack;
            if (SvSMAGICAL(ary)) {
                PUTBACK;
                mg_set((SV*)ary);
@@ -4676,3 +4825,13 @@ PP(pp_threadsv)
 {
     DIE(aTHX_ "tried to access per-thread data in non-threaded perl");
 }
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set ts=8 sts=4 sw=4 noet:
+ */