This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Plan 9: No Configure.
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 9237a8b..c9d1dc6 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -1,6 +1,6 @@
 /*    pp.c
  *
- *    Copyright (c) 1991-2001, Larry Wall
+ *    Copyright (c) 1991-2002, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -15,8 +15,9 @@
 #include "EXTERN.h"
 #define PERL_IN_PP_C
 #include "perl.h"
+#include "keywords.h"
 
-/* variations on pp_null */
+#include "reentr.h"
 
 /* XXX I can't imagine anyone who doesn't have this actually _needs_
    it, since pid_t is an integral type.
@@ -26,6 +27,8 @@
 extern Pid_t getpid (void);
 #endif
 
+/* variations on pp_null */
+
 PP(pp_stub)
 {
     dSP;
@@ -44,8 +47,9 @@ PP(pp_scalar)
 PP(pp_padav)
 {
     dSP; dTARGET;
+    I32 gimme;
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PL_curpad[PL_op->op_targ]);
+       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     EXTEND(SP, 1);
     if (PL_op->op_flags & OPf_REF) {
        PUSHs(TARG);
@@ -56,12 +60,13 @@ PP(pp_padav)
        PUSHs(TARG);
        RETURN;
     }
-    if (GIMME == G_ARRAY) {
+    gimme = GIMME_V;
+    if (gimme == G_ARRAY) {
        I32 maxarg = AvFILL((AV*)TARG) + 1;
        EXTEND(SP, maxarg);
        if (SvMAGICAL(TARG)) {
            U32 i;
-           for (i=0; i < maxarg; i++) {
+           for (i=0; i < (U32)maxarg; i++) {
                SV **svp = av_fetch((AV*)TARG, i, FALSE);
                SP[i+1] = (svp) ? *svp : &PL_sv_undef;
            }
@@ -71,7 +76,7 @@ PP(pp_padav)
        }
        SP += maxarg;
     }
-    else {
+    else if (gimme == G_SCALAR) {
        SV* sv = sv_newmortal();
        I32 maxarg = AvFILL((AV*)TARG) + 1;
        sv_setiv(sv, maxarg);
@@ -87,7 +92,7 @@ PP(pp_padhv)
 
     XPUSHs(TARG);
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PL_curpad[PL_op->op_targ]);
+       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     if (PL_op->op_flags & OPf_REF)
        RETURN;
     else if (LVRET) {
@@ -156,7 +161,7 @@ PP(pp_rv2gv)
                    GV *gv;
                    if (cUNOP->op_targ) {
                        STRLEN len;
-                       SV *namesv = PL_curpad[cUNOP->op_targ];
+                       SV *namesv = PAD_SV(cUNOP->op_targ);
                        name = SvPV(namesv, len);
                        gv = (GV*)NEWSV(0,0);
                        gv_init(gv, CopSTASH(PL_curcop), name, len, 0);
@@ -365,6 +370,8 @@ PP(pp_prototype)
                I32 oa;
                char str[ MAX_ARGS_OP * 2 + 2 ]; /* One ';', one '\0' */
 
+               if (code == -KEY_chop || code == -KEY_chomp)
+                   goto set;
                while (i < MAXO) {      /* The slow way. */
                    if (strEQ(s + 6, PL_op_name[i])
                        || strEQ(s + 6, PL_op_desc[i]))
@@ -415,7 +422,7 @@ PP(pp_prototype)
 PP(pp_anoncode)
 {
     dSP;
-    CV* cv = (CV*)PL_curpad[PL_op->op_targ];
+    CV* cv = (CV*)PAD_SV(PL_op->op_targ);
     if (CvCLONE(cv))
        cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
     EXTEND(SP,1);
@@ -516,7 +523,7 @@ PP(pp_bless)
            Perl_croak(aTHX_ "Attempt to bless into a reference");
        ptr = SvPV(ssv,len);
        if (ckWARN(WARN_MISC) && len == 0)
-           Perl_warner(aTHX_ WARN_MISC,
+           Perl_warner(aTHX_ packWARN(WARN_MISC),
                   "Explicit blessing to '' (assuming package main)");
        stash = gv_stashpvn(ptr, len, TRUE);
     }
@@ -759,8 +766,7 @@ PP(pp_undef)
     if (!sv)
        RETPUSHUNDEF;
 
-    if (SvTHINKFIRST(sv))
-       sv_force_normal(sv);
+    SV_CHECK_THINKFIRST_COW_DROP(sv);
 
     switch (SvTYPE(sv)) {
     case SVt_NULL:
@@ -773,7 +779,7 @@ PP(pp_undef)
        break;
     case SVt_PVCV:
        if (ckWARN(WARN_MISC) && cv_const_sv((CV*)sv))
-           Perl_warner(aTHX_ WARN_MISC, "Constant subroutine %s undefined",
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "Constant subroutine %s undefined",
                 CvANON((CV*)sv) ? "(anonymous)" : GvENAME(CvGV((CV*)sv)));
        /* FALL THROUGH */
     case SVt_PVFM:
@@ -874,10 +880,100 @@ PP(pp_postdec)
 PP(pp_pow)
 {
     dSP; dATARGET; tryAMAGICbin(pow,opASSIGN);
+#ifdef PERL_PRESERVE_IVUV
+    /* ** is implemented with pow. pow is floating point. Perl programmers
+       write 2 ** 31 and expect it to be 2147483648
+       pow never made any guarantee to deliver a result to 53 (or whatever)
+       bits of accuracy. Which is unfortunate, as perl programmers expect it
+       to, and on some platforms (eg Irix with long doubles) it doesn't in
+       a very visible case. (2 ** 31, which a regression test uses)
+       So we'll implement power-of-2 ** +ve integer with multiplies, to avoid
+       these problems.  */
     {
-      dPOPTOPnnrl;
-      SETn( Perl_pow( left, right) );
-      RETURN;
+        SvIV_please(TOPm1s);
+        if (SvIOK(TOPm1s)) {
+            bool baseuok = SvUOK(TOPm1s);
+            UV baseuv;
+
+            if (baseuok) {
+                baseuv = SvUVX(TOPm1s);
+            } else {
+                IV iv = SvIVX(TOPm1s);
+                if (iv >= 0) {
+                    baseuv = iv;
+                    baseuok = TRUE; /* effectively it's a UV now */
+                } else {
+                    baseuv = -iv; /* abs, baseuok == false records sign */
+                }
+            }
+            SvIV_please(TOPs);
+            if (SvIOK(TOPs)) {
+                UV power;
+
+                if (SvUOK(TOPs)) {
+                    power = SvUVX(TOPs);
+                } else {
+                    IV iv = SvIVX(TOPs);
+                    if (iv >= 0) {
+                        power = iv;
+                    } else {
+                        goto float_it; /* Can't do negative powers this way.  */
+                    }
+                }
+                /* now we have integer ** positive integer.
+                   foo & (foo - 1) is zero only for a power of 2.  */
+                if (!(baseuv & (baseuv - 1))) {
+                    /* We are raising power-of-2 to postive integer.
+                       The logic here will work for any base (even non-integer
+                       bases) but it can be less accurate than
+                       pow (base,power) or exp (power * log (base)) when the
+                       intermediate values start to spill out of the mantissa.
+                       With powers of 2 we know this can't happen.
+                       And powers of 2 are the favourite thing for perl
+                       programmers to notice ** not doing what they mean. */
+                    NV result = 1.0;
+                    NV base = baseuok ? baseuv : -(NV)baseuv;
+                    int n = 0;
+
+                    /* The logic is this.
+                       x ** n === x ** m1 * x ** m2 where n = m1 + m2
+                       so as 42 is 32 + 8 + 2
+                       x ** 42 can be written as
+                       x ** 32 * x ** 8 * x ** 2
+                       I can calculate x ** 2, x ** 4, x ** 8 etc trivially:
+                       x ** 2n is x ** n * x ** n
+                       So I loop round, squaring x each time
+                       (x, x ** 2, x ** 4, x ** 8) and multiply the result
+                       by the x-value whenever that bit is set in the power.
+                       To finish as soon as possible I zero bits in the power
+                       when I've done them, so that power becomes zero when
+                       I clear the last bit (no more to do), and the loop
+                       terminates.  */
+                    for (; power; base *= base, n++) {
+                        /* Do I look like I trust gcc with long longs here?
+                           Do I hell.  */
+                        UV bit = (UV)1 << (UV)n;
+                        if (power & bit) {
+                            result *= base;
+                            /* Only bother to clear the bit if it is set.  */
+                            power &= ~bit;
+                           /* Avoid squaring base again if we're done. */
+                           if (power == 0) break;
+                        }
+                    }
+                    SP--;
+                    SETn( result );
+                    RETURN;
+                }
+            }
+        }
+    }
+      float_it:
+#endif    
+    {
+        dPOPTOPnnrl;
+        SETn( Perl_pow( left, right) );
+        RETURN;
     }
 }
 
@@ -1003,7 +1099,7 @@ PP(pp_divide)
 {
     dSP; dATARGET; tryAMAGICbin(div,opASSIGN);
     /* Only try to do UV divide first
-       if ((SLOPPYDIVIDE is true) or 
+       if ((SLOPPYDIVIDE is true) or
            (PERL_PRESERVE_IVUV is true and one or both SV is a UV too large
             to preserve))
        The assumption is that it is better to use floating point divide
@@ -1094,7 +1190,7 @@ PP(pp_divide)
                     }
                     /* 2s complement assumption */
                     if (result <= (UV)IV_MIN)
-                        SETi( -result );
+                        SETi( -(IV)result );
                     else {
                         /* It's exact but too negative for IV. */
                         SETn( -(NV)result );
@@ -1120,8 +1216,8 @@ PP(pp_modulo)
     {
        UV left  = 0;
        UV right = 0;
-       bool left_neg;
-       bool right_neg;
+       bool left_neg = FALSE;
+       bool right_neg = FALSE;
        bool use_double = FALSE;
        bool dright_valid = FALSE;
        NV dright = 0.0;
@@ -1386,7 +1482,7 @@ PP(pp_subtract)
                    buv = (UV)-biv;
            }
            /* ?uvok if value is >= 0. basically, flagged as UV if it's +ve,
-              else "IV" now, independant of how it came in.
+              else "IV" now, independent of how it came in.
               if a, b represents positive, A, B negative, a maps to -A etc
               a - b =>  (a - b)
               A - b => -(a + b)
@@ -2255,7 +2351,7 @@ PP(pp_complement)
              while (tmps < send) {
                  UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
                  tmps += UTF8SKIP(tmps);
-                 result = uvchr_to_utf8(result, ~c);
+                 result = uvchr_to_utf8_flags(result, ~c, UNICODE_ALLOW_ANY);
              }
              *result = '\0';
              result -= targlen;
@@ -2589,7 +2685,7 @@ S_seed(pTHX)
     u = (U32)SEED_C1 * when[0] + (U32)SEED_C2 * when[1];
 #else
 #  ifdef HAS_GETTIMEOFDAY
-    gettimeofday(&when,(struct timezone *) 0);
+    PerlProc_gettimeofday(&when,NULL);
     u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec;
 #  else
     (void)time(&when);
@@ -2624,7 +2720,7 @@ PP(pp_log)
       value = POPn;
       if (value <= 0.0) {
        SET_NUMERIC_STANDARD();
-       DIE(aTHX_ "Can't take log of %g", value);
+       DIE(aTHX_ "Can't take log of %"NVgf, value);
       }
       value = Perl_log(value);
       XPUSHn(value);
@@ -2640,7 +2736,7 @@ PP(pp_sqrt)
       value = POPn;
       if (value < 0.0) {
        SET_NUMERIC_STANDARD();
-       DIE(aTHX_ "Can't take sqrt of %g", value);
+       DIE(aTHX_ "Can't take sqrt of %"NVgf, value);
       }
       value = Perl_sqrt(value);
       XPUSHn(value);
@@ -2699,7 +2795,7 @@ PP(pp_int)
 #   if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
 #       ifdef HAS_MODFL_POW32_BUG
 /* some versions of glibc split (i + d) into (i-1, d+1) for 2^32 <= i < 2^64 */
-                { 
+                {
                     NV offset = Perl_modf(value, &value);
                     (void)Perl_modf(offset, &offset);
                     value += offset;
@@ -2788,8 +2884,18 @@ PP(pp_hex)
     STRLEN len;
     NV result_nv;
     UV result_uv;
+    SV* sv = POPs;
 
-    tmps = (SvPVx(POPs, len));
+    tmps = (SvPVx(sv, len));
+    if (DO_UTF8(sv)) {
+        /* If Unicode, try to downgrade
+         * If not possible, croak. */
+         SV* tsv = sv_2mortal(newSVsv(sv));
+       
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
+    }
     result_uv = grok_hex (tmps, &len, &flags, &result_nv);
     if (flags & PERL_SCAN_GREATER_THAN_UV_MAX) {
         XPUSHn(result_nv);
@@ -2808,8 +2914,18 @@ PP(pp_oct)
     STRLEN len;
     NV result_nv;
     UV result_uv;
+    SV* sv = POPs;
 
-    tmps = (SvPVx(POPs, len));
+    tmps = (SvPVx(sv, len));
+    if (DO_UTF8(sv)) {
+        /* If Unicode, try to downgrade
+         * If not possible, croak. */
+         SV* tsv = sv_2mortal(newSVsv(sv));
+       
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
+    }
     while (*tmps && len && isSPACE(*tmps))
         tmps++, len--;
     if (*tmps == '0')
@@ -2933,7 +3049,7 @@ PP(pp_substr)
        if (lvalue || repl)
            Perl_croak(aTHX_ "substr outside of string");
        if (ckWARN(WARN_SUBSTR))
-           Perl_warner(aTHX_ WARN_SUBSTR, "substr outside of string");
+           Perl_warner(aTHX_ packWARN(WARN_SUBSTR), "substr outside of string");
        RETPUSHUNDEF;
     }
     else {
@@ -2969,7 +3085,7 @@ PP(pp_substr)
                    STRLEN n_a;
                    SvPV_force(sv,n_a);
                    if (ckWARN(WARN_SUBSTR))
-                       Perl_warner(aTHX_ WARN_SUBSTR,
+                       Perl_warner(aTHX_ packWARN(WARN_SUBSTR),
                                "Attempt to use reference as lvalue in substr");
                }
                if (SvOK(sv))           /* is it defined ? */
@@ -3050,7 +3166,7 @@ PP(pp_index)
        sv_pos_u2b(big, &offset, 0);
     if (offset < 0)
        offset = 0;
-    else if (offset > biglen)
+    else if (offset > (I32)biglen)
        offset = biglen;
     if (!(tmps2 = fbm_instr((unsigned char*)tmps + offset,
       (unsigned char*)tmps + biglen, little, 0)))
@@ -3091,7 +3207,7 @@ PP(pp_rindex)
     }
     if (offset < 0)
        offset = 0;
-    else if (offset > blen)
+    else if (offset > (I32)blen)
        offset = blen;
     if (!(tmps2 = rninstr(tmps,  tmps  + offset,
                          tmps2, tmps2 + llen)))
@@ -3124,14 +3240,16 @@ PP(pp_ord)
     U8 *s = (U8*)SvPVx(argsv, len);
     SV *tmpsv;
 
-    if (PL_encoding && !DO_UTF8(argsv)) {
+    if (PL_encoding && SvPOK(argsv) && !DO_UTF8(argsv)) {
         tmpsv = sv_2mortal(newSVsv(argsv));
-        s = (U8*)Perl_sv_recode_to_utf8(aTHX_ tmpsv, PL_encoding);
+        s = (U8*)sv_recode_to_utf8(tmpsv, PL_encoding);
         argsv = tmpsv;
     }
 
-    XPUSHu(DO_UTF8(argsv) ? utf8_to_uvchr(s, 0) : (*s & 0xff));
-    
+    XPUSHu(DO_UTF8(argsv) ?
+          utf8n_to_uvchr(s, UTF8_MAXLEN, 0, UTF8_ALLOW_ANYUV) :
+          (*s & 0xff));
+
     RETURN;
 }
 
@@ -3144,8 +3262,8 @@ PP(pp_chr)
     (void)SvUPGRADE(TARG,SVt_PV);
 
     if (value > 255 && !IN_BYTES) {
-       SvGROW(TARG, UNISKIP(value)+1);
-       tmps = (char*)uvchr_to_utf8((U8*)SvPVX(TARG), value);
+       SvGROW(TARG, (STRLEN)UNISKIP(value)+1);
+       tmps = (char*)uvchr_to_utf8_flags((U8*)SvPVX(TARG), value, 0);
        SvCUR_set(TARG, tmps - SvPVX(TARG));
        *tmps = '\0';
        (void)SvPOK_only(TARG);
@@ -3157,11 +3275,22 @@ PP(pp_chr)
     SvGROW(TARG,2);
     SvCUR_set(TARG, 1);
     tmps = SvPVX(TARG);
-    *tmps++ = value;
+    *tmps++ = (char)value;
     *tmps = '\0';
     (void)SvPOK_only(TARG);
-    if (PL_encoding)
-        Perl_sv_recode_to_utf8(aTHX_ TARG, PL_encoding);
+    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);
+           SvCUR_set(TARG, 2);
+           *tmps++ = (U8)UTF8_EIGHT_BIT_HI(value);
+           *tmps++ = (U8)UTF8_EIGHT_BIT_LO(value);
+           *tmps = '\0';
+           SvUTF8_on(TARG);
+       }
+    }
     XPUSHs(TARG);
     RETURN;
 }
@@ -3174,32 +3303,28 @@ PP(pp_crypt)
     STRLEN n_a;
     STRLEN len;
     char *tmps = SvPV(left, len);
-    char *t    = 0;
+
     if (DO_UTF8(left)) {
-         /* If Unicode take the crypt() of the low 8 bits
-         * of the characters of the string. */
-        char *s    = tmps;
-        char *send = tmps + len;
-        STRLEN i   = 0;
-        Newz(688, t, len, char);
-        while (s < send) {
-             t[i++] = utf8_to_uvchr((U8*)s, 0) & 0xFF;
-             s += UTF8SKIP(s);
-        }
-        tmps = t;
+         /* If Unicode, try to downgrade.
+         * If not possible, croak.
+         * Yes, we made this up.  */
+         SV* tsv = sv_2mortal(newSVsv(left));
+
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
     }
 #   ifdef FCRYPT
     sv_setpv(TARG, fcrypt(tmps, SvPV(right, n_a)));
 #   else
     sv_setpv(TARG, PerlProc_crypt(tmps, SvPV(right, n_a)));
 #   endif
-    Safefree(t);
+    SETs(TARG);
+    RETURN;
 #else
     DIE(aTHX_
       "The crypt() function is unimplemented due to excessive paranoia.");
 #endif
-    SETs(TARG);
-    RETURN;
 }
 
 PP(pp_ucfirst)
@@ -3209,26 +3334,35 @@ PP(pp_ucfirst)
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv)) {
+    SvGETMAGIC(sv);
+    if (DO_UTF8(sv) &&
+       (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
+       UTF8_IS_START(*s)) {
        U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
        STRLEN ulen;
        STRLEN tculen;
 
-       s = (U8*)SvPV(sv, slen);
        utf8_to_uvchr(s, &ulen);
-
        toTITLE_utf8(s, tmpbuf, &tculen);
        utf8_to_uvchr(tmpbuf, 0);
 
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
+           /* 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.
+            * tculen is the byte length of the freshly titlecased
+            * Unicode character stored as UTF-8 at tmpbuf.
+            * We first set the result to be the titlecased character,
+            * and then append the rest of the SV data. */
            sv_setpvn(TARG, (char*)tmpbuf, tculen);
-           sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
+           if (slen > ulen)
+               sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
            SvUTF8_on(TARG);
            SETs(TARG);
        }
        else {
-           s = (U8*)SvPV_force(sv, slen);
+           s = (U8*)SvPV_force_nomg(sv, slen);
            Copy(tmpbuf, s, tculen, U8);
        }
     }
@@ -3236,11 +3370,11 @@ PP(pp_ucfirst)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, slen);
+       s = (U8*)SvPV_force_nomg(sv, slen);
        if (*s) {
            if (IN_LOCALE_RUNTIME) {
                TAINT;
@@ -3251,8 +3385,7 @@ PP(pp_ucfirst)
                *s = toUPPER(*s);
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3263,7 +3396,10 @@ PP(pp_lcfirst)
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && UTF8_IS_START(*s)) {
+    SvGETMAGIC(sv);
+    if (DO_UTF8(sv) &&
+       (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
+       UTF8_IS_START(*s)) {
        STRLEN ulen;
        U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
        U8 *tend;
@@ -3271,18 +3407,18 @@ PP(pp_lcfirst)
 
        toLOWER_utf8(s, tmpbuf, &ulen);
        uv = utf8_to_uvchr(tmpbuf, 0);
-       
        tend = uvchr_to_utf8(tmpbuf, uv);
 
-       if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
+       if (!SvPADTMP(sv) || (STRLEN)(tend - tmpbuf) != ulen || SvREADONLY(sv)) {
            dTARGET;
            sv_setpvn(TARG, (char*)tmpbuf, tend - tmpbuf);
-           sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
+           if (slen > ulen)
+               sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
            SvUTF8_on(TARG);
            SETs(TARG);
        }
        else {
-           s = (U8*)SvPV_force(sv, slen);
+           s = (U8*)SvPV_force_nomg(sv, slen);
            Copy(tmpbuf, s, ulen, U8);
        }
     }
@@ -3290,11 +3426,11 @@ PP(pp_lcfirst)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, slen);
+       s = (U8*)SvPV_force_nomg(sv, slen);
        if (*s) {
            if (IN_LOCALE_RUNTIME) {
                TAINT;
@@ -3305,8 +3441,7 @@ PP(pp_lcfirst)
                *s = toLOWER(*s);
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3317,6 +3452,7 @@ PP(pp_uc)
     register U8 *s;
     STRLEN len;
 
+    SvGETMAGIC(sv);
     if (DO_UTF8(sv)) {
        dTARGET;
        STRLEN ulen;
@@ -3324,15 +3460,17 @@ PP(pp_uc)
        U8 *send;
        U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
 
-       s = (U8*)SvPV(sv,len);
+       s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
            SvUTF8_off(TARG);                           /* decontaminate */
            sv_setpvn(TARG, "", 0);
            SETs(TARG);
        }
        else {
+           STRLEN nchar = utf8_length(s, s + len);
+
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (len * 2) + 1);
+           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
@@ -3352,11 +3490,11 @@ PP(pp_uc)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, len);
+       s = (U8*)SvPV_force_nomg(sv, len);
        if (len) {
            register U8 *send = s + len;
 
@@ -3372,8 +3510,7 @@ PP(pp_uc)
            }
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3384,6 +3521,7 @@ PP(pp_lc)
     register U8 *s;
     STRLEN len;
 
+    SvGETMAGIC(sv);
     if (DO_UTF8(sv)) {
        dTARGET;
        STRLEN ulen;
@@ -3391,15 +3529,17 @@ PP(pp_lc)
        U8 *send;
        U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
 
-       s = (U8*)SvPV(sv,len);
+       s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
            SvUTF8_off(TARG);                           /* decontaminate */
            sv_setpvn(TARG, "", 0);
            SETs(TARG);
        }
        else {
+           STRLEN nchar = utf8_length(s, s + len);
+
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (len * 2) + 1);
+           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
@@ -3436,12 +3576,12 @@ PP(pp_lc)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
 
-       s = (U8*)SvPV_force(sv, len);
+       s = (U8*)SvPV_force_nomg(sv, len);
        if (len) {
            register U8 *send = s + len;
 
@@ -3457,8 +3597,7 @@ PP(pp_lc)
            }
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3566,22 +3705,21 @@ PP(pp_each)
     HV *hash = (HV*)POPs;
     HE *entry;
     I32 gimme = GIMME_V;
-    I32 realhv = (SvTYPE(hash) == SVt_PVHV);
 
     PUTBACK;
     /* might clobber stack_sp */
-    entry = realhv ? hv_iternext(hash) : avhv_iternext((AV*)hash);
+    entry = hv_iternext(hash);
     SPAGAIN;
 
     EXTEND(SP, 2);
     if (entry) {
-       PUSHs(hv_iterkeysv(entry));     /* won't clobber stack_sp */
+        SV* sv = hv_iterkeysv(entry);
+       PUSHs(sv);      /* won't clobber stack_sp */
        if (gimme == G_ARRAY) {
            SV *val;
            PUTBACK;
            /* might clobber stack_sp */
-           val = realhv ?
-                 hv_iterval(hash, entry) : avhv_iterval((AV*)hash, entry);
+           val = hv_iterval(hash, entry);
            SPAGAIN;
            PUSHs(val);
        }
@@ -3621,19 +3759,13 @@ PP(pp_delete)
                *MARK = sv ? sv : &PL_sv_undef;
            }
        }
-       else if (hvtype == SVt_PVAV) {
-           if (PL_op->op_flags & OPf_SPECIAL) {        /* array element */
-               while (++MARK <= SP) {
-                   sv = av_delete((AV*)hv, SvIV(*MARK), discard);
-                   *MARK = sv ? sv : &PL_sv_undef;
-               }
-           }
-           else {                                      /* pseudo-hash element */
-               while (++MARK <= SP) {
-                   sv = avhv_delete_ent((AV*)hv, *MARK, discard, 0);
-                   *MARK = sv ? sv : &PL_sv_undef;
-               }
-           }
+       else if (hvtype == SVt_PVAV) {                  /* array element */
+            if (PL_op->op_flags & OPf_SPECIAL) {
+                while (++MARK <= SP) {
+                    sv = av_delete((AV*)hv, SvIV(*MARK), discard);
+                    *MARK = sv ? sv : &PL_sv_undef;
+                }
+            }
        }
        else
            DIE(aTHX_ "Not a HASH reference");
@@ -3654,7 +3786,7 @@ PP(pp_delete)
            if (PL_op->op_flags & OPf_SPECIAL)
                sv = av_delete((AV*)hv, SvIV(keysv), discard);
            else
-               sv = avhv_delete_ent((AV*)hv, keysv, discard, 0);
+               DIE(aTHX_ "panic: avhv_delete no longer supported");
        }
        else
            DIE(aTHX_ "Not a HASH reference");
@@ -3694,8 +3826,6 @@ PP(pp_exists)
            if (av_exists((AV*)hv, SvIV(tmpsv)))
                RETPUSHYES;
        }
-       else if (avhv_exists_ent((AV*)hv, tmpsv, 0))    /* pseudo-hash element */
-           RETPUSHYES;
     }
     else {
        DIE(aTHX_ "Not a HASH reference");
@@ -3708,42 +3838,53 @@ PP(pp_hslice)
     dSP; dMARK; dORIGMARK;
     register HV *hv = (HV*)POPs;
     register I32 lval = (PL_op->op_flags & OPf_MOD || LVRET);
-    I32 realhv = (SvTYPE(hv) == SVt_PVHV);
+    bool localizing = PL_op->op_private & OPpLVAL_INTRO ? TRUE : FALSE;
+    bool other_magic = FALSE;
+
+    if (localizing) {
+        MAGIC *mg;
+        HV *stash;
+
+        other_magic = mg_find((SV*)hv, PERL_MAGIC_env) ||
+            ((mg = mg_find((SV*)hv, PERL_MAGIC_tied))
+             /* Try to preserve the existenceness of a tied hash
+              * element by using EXISTS and DELETE if possible.
+              * Fallback to FETCH and STORE otherwise */
+             && (stash = SvSTASH(SvRV(SvTIED_obj((SV*)hv, mg))))
+             && gv_fetchmethod_autoload(stash, "EXISTS", TRUE)
+             && gv_fetchmethod_autoload(stash, "DELETE", TRUE));
+    }
+
+    while (++MARK <= SP) {
+        SV *keysv = *MARK;
+        SV **svp;
+        HE *he;
+        bool preeminent = FALSE;
+
+        if (localizing) {
+            preeminent = SvRMAGICAL(hv) && !other_magic ? 1 :
+                hv_exists_ent(hv, keysv, 0);
+        }
 
-    if (!realhv && PL_op->op_private & OPpLVAL_INTRO)
-       DIE(aTHX_ "Can't localize pseudo-hash element");
+        he = hv_fetch_ent(hv, keysv, lval, 0);
+        svp = he ? &HeVAL(he) : 0;
 
-    if (realhv || SvTYPE(hv) == SVt_PVAV) {
-       while (++MARK <= SP) {
-           SV *keysv = *MARK;
-           SV **svp;
-           I32 preeminent = SvRMAGICAL(hv) ? 1 :
-                               realhv ? hv_exists_ent(hv, keysv, 0)
-                                      : avhv_exists_ent((AV*)hv, keysv, 0);
-           if (realhv) {
-               HE *he = hv_fetch_ent(hv, keysv, lval, 0);
-               svp = he ? &HeVAL(he) : 0;
-           }
-           else {
-               svp = avhv_fetch_ent((AV*)hv, keysv, lval, 0);
-           }
-           if (lval) {
-               if (!svp || *svp == &PL_sv_undef) {
-                   STRLEN n_a;
-                   DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
-               }
-               if (PL_op->op_private & OPpLVAL_INTRO) {
-                   if (preeminent)
-                       save_helem(hv, keysv, svp);
-                   else {
-                       STRLEN keylen;
-                       char *key = SvPV(keysv, keylen);
-                       SAVEDELETE(hv, savepvn(key,keylen), keylen);
-                   }
+        if (lval) {
+            if (!svp || *svp == &PL_sv_undef) {
+                STRLEN n_a;
+                DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
+            }
+            if (localizing) {
+                if (preeminent)
+                    save_helem(hv, keysv, svp);
+                else {
+                    STRLEN keylen;
+                    char *key = SvPV(keysv, keylen);
+                    SAVEDELETE(hv, savepvn(key,keylen), keylen);
                 }
-           }
-           *MARK = svp ? *svp : &PL_sv_undef;
-       }
+            }
+        }
+        *MARK = svp ? *svp : &PL_sv_undef;
     }
     if (GIMME != G_ARRAY) {
        MARK = ORIGMARK;
@@ -3844,7 +3985,7 @@ PP(pp_anonhash)
        if (MARK < SP)
            sv_setsv(val, *++MARK);
        else if (ckWARN(WARN_MISC))
-           Perl_warner(aTHX_ WARN_MISC, "Odd number of elements in hash assignment");
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "Odd number of elements in anonymous hash");
        (void)hv_store_ent(hv,key,val,0);
     }
     SP = ORIGMARK;
@@ -3903,8 +4044,11 @@ PP(pp_splice)
        offset = 0;
        length = AvMAX(ary) + 1;
     }
-    if (offset > AvFILLp(ary) + 1)
+    if (offset > AvFILLp(ary) + 1) {
+       if (ckWARN(WARN_MISC))
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "splice() offset past end of array" );
        offset = AvFILLp(ary) + 1;
+    }
     after = AvFILLp(ary) + 1 - (offset + length);
     if (after < 0) {                           /* not that much array */
        length += after;                        /* offset+length now in array */
@@ -4187,7 +4331,7 @@ PP(pp_reverse)
                        while (down > up) {
                            tmp = *up;
                            *up++ = *down;
-                           *down-- = tmp;
+                           *down-- = (char)tmp;
                        }
                    }
                }
@@ -4197,7 +4341,7 @@ PP(pp_reverse)
            while (down > up) {
                tmp = *up;
                *up++ = *down;
-               *down-- = tmp;
+               *down-- = (char)tmp;
            }
            (void)SvPOK_only_UTF8(TARG);
        }
@@ -4251,17 +4395,13 @@ PP(pp_split)
 
     if (pm->op_pmreplroot) {
 #ifdef USE_ITHREADS
-       ary = GvAVn((GV*)PL_curpad[INT2PTR(PADOFFSET, pm->op_pmreplroot)]);
+       ary = GvAVn((GV*)PAD_SVl(INT2PTR(PADOFFSET, pm->op_pmreplroot)));
 #else
        ary = GvAVn((GV*)pm->op_pmreplroot);
 #endif
     }
     else if (gimme != G_ARRAY)
-#ifdef USE_5005THREADS
-       ary = (AV*)PL_curpad[0];
-#else
        ary = GvAVn(PL_defgv);
-#endif /* USE_5005THREADS */
     else
        ary = Nullav;
     if (ary && (gimme != G_ARRAY || (pm->op_pmflags & PMf_ONCE))) {
@@ -4427,7 +4567,7 @@ PP(pp_split)
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            if (rx->nparens) {
-               for (i = 1; i <= rx->nparens; i++) {
+               for (i = 1; i <= (I32)rx->nparens; i++) {
                    s = rx->startp[i] + orig;
                    m = rx->endp[i] + orig;
 
@@ -4469,8 +4609,12 @@ PP(pp_split)
        iters++;
     }
     else if (!origlimit) {
-       while (iters > 0 && (!TOPs || !SvANY(TOPs) || SvCUR(TOPs) == 0))
-           iters--, SP--;
+       while (iters > 0 && (!TOPs || !SvANY(TOPs) || SvCUR(TOPs) == 0)) {
+           if (TOPs && !make_mortal)
+               sv_2mortal(TOPs);
+           iters--;
+           SP--;
+       }
     }
 
     if (realarray) {
@@ -4517,38 +4661,12 @@ PP(pp_split)
     RETPUSHUNDEF;
 }
 
-#ifdef USE_5005THREADS
-void
-Perl_unlock_condpair(pTHX_ void *svv)
-{
-    MAGIC *mg = mg_find((SV*)svv, PERL_MAGIC_mutex);
-
-    if (!mg)
-       Perl_croak(aTHX_ "panic: unlock_condpair unlocking non-mutex");
-    MUTEX_LOCK(MgMUTEXP(mg));
-    if (MgOWNER(mg) != thr)
-       Perl_croak(aTHX_ "panic: unlock_condpair unlocking mutex that we don't own");
-    MgOWNER(mg) = 0;
-    COND_SIGNAL(MgOWNERCONDP(mg));
-    DEBUG_S(PerlIO_printf(Perl_debug_log, "0x%"UVxf": unlock 0x%"UVxf"\n",
-                         PTR2UV(thr), PTR2UV(svv)));
-    MUTEX_UNLOCK(MgMUTEXP(mg));
-}
-#endif /* USE_5005THREADS */
-
 PP(pp_lock)
 {
     dSP;
     dTOPss;
     SV *retsv = sv;
-#ifdef USE_5005THREADS
-    sv_lock(sv);
-#endif /* USE_5005THREADS */
-#ifdef USE_ITHREADS
-    shared_sv *ssv = Perl_sharedsv_find(aTHX_ sv);
-    if(ssv)
-        Perl_sharedsv_lock(aTHX_ ssv);
-#endif /* USE_ITHREADS */
+    SvLOCK(sv);
     if (SvTYPE(retsv) == SVt_PVAV || SvTYPE(retsv) == SVt_PVHV
        || SvTYPE(retsv) == SVt_PVCV) {
        retsv = refto(retsv);
@@ -4559,15 +4677,5 @@ PP(pp_lock)
 
 PP(pp_threadsv)
 {
-#ifdef USE_5005THREADS
-    dSP;
-    EXTEND(SP, 1);
-    if (PL_op->op_private & OPpLVAL_INTRO)
-       PUSHs(*save_threadsv(PL_op->op_targ));
-    else
-       PUSHs(THREADSV(PL_op->op_targ));
-    RETURN;
-#else
     DIE(aTHX_ "tried to access per-thread data in non-threaded perl");
-#endif /* USE_5005THREADS */
 }