This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add isUTF8_CHAR_flags() macro
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index e6eb799..ea49b01 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -66,7 +66,7 @@ PP(pp_stub)
 PP(pp_padav)
 {
     dSP; dTARGET;
-    I32 gimme;
+    U8 gimme;
     assert(SvTYPE(TARG) == SVt_PVAV);
     if (UNLIKELY( PL_op->op_private & OPpLVAL_INTRO ))
        if (LIKELY( !(PL_op->op_private & OPpPAD_STATE) ))
@@ -121,7 +121,7 @@ PP(pp_padav)
 PP(pp_padhv)
 {
     dSP; dTARGET;
-    I32 gimme;
+    U8 gimme;
 
     assert(SvTYPE(TARG) == SVt_PVHV);
     XPUSHs(TARG);
@@ -675,8 +675,6 @@ PP(pp_gelem)
            break;
        case 'F':
            if (len == 10 && strEQ(second_letter, "ILEHANDLE")) {
-               /* finally deprecated in 5.8.0 */
-               deprecate("*glob{FILEHANDLE}");
                tmpRef = MUTABLE_SV(GvIOp(gv));
            }
            else
@@ -811,17 +809,6 @@ S_do_chomp(pTHX_ SV *retval, SV *sv, bool chomping)
             Perl_croak_no_modify();
     }
 
-    if (IN_ENCODING) {
-       if (!SvUTF8(sv)) {
-           /* XXX, here sv is utf8-ized as a side-effect!
-              If encoding.pm is used properly, almost string-generating
-              operations, including literal strings, chr(), input data, etc.
-              should have been utf8-ized already, right?
-           */
-           sv_recode_to_utf8(sv, _get_encoding());
-       }
-    }
-
     s = SvPV(sv, len);
     if (chomping) {
        if (s && len) {
@@ -863,14 +850,6 @@ S_do_chomp(pTHX_ SV *retval, SV *sv, bool chomping)
                        }
                        rsptr = temp_buffer;
                    }
-                   else if (IN_ENCODING) {
-                       /* RS is 8 bit, encoding.pm is used.
-                        * Do not recode PL_rs as a side-effect. */
-                       svrecode = newSVpvn(rsptr, rslen);
-                       sv_recode_to_utf8(svrecode, _get_encoding());
-                       rsptr = SvPV_const(svrecode, rslen);
-                       rs_charlen = sv_len_utf8(svrecode);
-                   }
                    else {
                        /* RS is 8 bit, scalar is utf8.  */
                        temp_buffer = (char*)bytes_to_utf8((U8*)rsptr, &rslen);
@@ -1366,9 +1345,14 @@ PP(pp_multiply)
             NV nr = SvNVX(svr);
             NV result;
 
-            il = (IV)nl;
-            ir = (IV)nr;
-            if (nl == (NV)il && nr == (NV)ir)
+            if (
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+                !Perl_isnan(nl) && nl == (NV)(il = (IV)nl)
+                && !Perl_isnan(nr) && nr == (NV)(ir = (IV)nr)
+#else
+                nl == (NV)(il = (IV)nl) && nr == (NV)(ir = (IV)nr)
+#endif
+                )
                 /* nothing was lost by converting to IVs */
                 goto do_iv;
             SP--;
@@ -1942,9 +1926,14 @@ PP(pp_subtract)
             NV nl = SvNVX(svl);
             NV nr = SvNVX(svr);
 
-            il = (IV)nl;
-            ir = (IV)nr;
-            if (nl == (NV)il && nr == (NV)ir)
+            if (
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+                !Perl_isnan(nl) && nl == (NV)(il = (IV)nl)
+                && !Perl_isnan(nr) && nr == (NV)(ir = (IV)nr)
+#else
+                nl == (NV)(il = (IV)nl) && nr == (NV)(ir = (IV)nr)
+#endif
+                )
                 /* nothing was lost by converting to IVs */
                 goto do_iv;
             SP--;
@@ -2642,6 +2631,8 @@ S_scomplement(pTHX_ SV *targ, SV *sv)
              U8 *result;
              U8 *p;
 
+              Perl_ck_warner_d(aTHX_ packWARN(WARN_DEPRECATED),
+                        deprecated_above_ff_msg, PL_op_desc[PL_op->op_type]);
              Newx(result, targlen + 1, U8);
              p = result;
              while (tmps < send) {
@@ -2775,13 +2766,7 @@ PP(pp_i_divide)
     }
 }
 
-#if defined(__GLIBC__) && IVSIZE == 8 && !defined(PERL_DEBUG_READONLY_OPS) \
-    && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
-STATIC
-PP(pp_i_modulo_0)
-#else
 PP(pp_i_modulo)
-#endif
 {
      /* This is the vanilla old i_modulo. */
      dSP; dATARGET;
@@ -2799,11 +2784,10 @@ PP(pp_i_modulo)
      }
 }
 
-#if defined(__GLIBC__) && IVSIZE == 8 && !defined(PERL_DEBUG_READONLY_OPS) \
+#if defined(__GLIBC__) && IVSIZE == 8 \
     && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
-STATIC
-PP(pp_i_modulo_1)
 
+PP(pp_i_modulo_glibc_bugfix)
 {
      /* This is the i_modulo with the workaround for the _moddi3 bug
       * in (at least) glibc 2.2.5 (the PERL_ABS() the workaround).
@@ -2822,49 +2806,6 @@ PP(pp_i_modulo_1)
          RETURN;
      }
 }
-
-PP(pp_i_modulo)
-{
-     dVAR; dSP; dATARGET;
-     tryAMAGICbin_MG(modulo_amg, AMGf_assign);
-     {
-         dPOPTOPiirl_nomg;
-         if (!right)
-              DIE(aTHX_ "Illegal modulus zero");
-         /* The assumption is to use hereafter the old vanilla version... */
-         PL_op->op_ppaddr =
-              PL_ppaddr[OP_I_MODULO] =
-                  Perl_pp_i_modulo_0;
-         /* .. but if we have glibc, we might have a buggy _moddi3
-          * (at least glibc 2.2.5 is known to have this bug), in other
-          * words our integer modulus with negative quad as the second
-          * argument might be broken.  Test for this and re-patch the
-          * 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. */
-         {
-              IV l =   3;
-              IV r = -10;
-              /* Cannot do this check with inlined IV constants since
-               * that seems to work correctly even with the buggy glibc. */
-              if (l % r == -3) {
-                   /* Yikes, we have the bug.
-                    * Patch in the workaround version. */
-                   PL_op->op_ppaddr =
-                        PL_ppaddr[OP_I_MODULO] =
-                            &Perl_pp_i_modulo_1;
-                   /* Make certain we work right this time, too. */
-                   right = PERL_ABS(right);
-              }
-         }
-         /* avoid FPE_INTOVF on some platforms when left is IV_MIN */
-         if (right == -1)
-             SETi( 0 );
-         else
-             SETi( left % right );
-         RETURN;
-     }
-}
 #endif
 
 PP(pp_i_add)
@@ -3207,7 +3148,7 @@ PP(pp_abs)
            } else {
              /* 2s complement assumption. Also, not really needed as
                 IV_MIN and -IV_MIN should both be %100...00 and NV-able  */
-             SETu(IV_MIN);
+             SETu((UV)IV_MIN);
            }
          }
        }
@@ -3584,7 +3525,7 @@ PP(pp_index)
     little_utf8 = DO_UTF8(little);
     if (big_utf8 ^ little_utf8) {
        /* One needs to be upgraded.  */
-       if (little_utf8 && !IN_ENCODING) {
+       if (little_utf8) {
            /* Well, maybe instead we might be able to downgrade the small
               string?  */
            char * const pv = (char*)bytes_from_utf8((U8 *)little_p, &llen,
@@ -3603,22 +3544,11 @@ PP(pp_index)
            sv_usepvn(temp, pv, llen);
            little_p = SvPVX(little);
        } else {
-           temp = little_utf8
-               ? newSVpvn(big_p, biglen) : newSVpvn(little_p, llen);
+           temp = newSVpvn(little_p, llen);
 
-           if (IN_ENCODING) {
-               sv_recode_to_utf8(temp, _get_encoding());
-           } else {
-               sv_utf8_upgrade(temp);
-           }
-           if (little_utf8) {
-               big = temp;
-               big_utf8 = TRUE;
-               big_p = SvPV_const(big, biglen);
-           } else {
-               little = temp;
-               little_p = SvPV_const(little, llen);
-           }
+           sv_utf8_upgrade(temp);
+           little = temp;
+           little_p = SvPV_const(little, llen);
        }
     }
     if (SvGAMAGIC(big)) {
@@ -3692,13 +3622,6 @@ PP(pp_ord)
     STRLEN len;
     const U8 *s = (U8*)SvPV_const(argsv, len);
 
-    if (IN_ENCODING && SvPOK(argsv) && !DO_UTF8(argsv)) {
-        SV * const tmpsv = sv_2mortal(newSVsv(argsv));
-        s = (U8*)sv_recode_to_utf8(tmpsv, _get_encoding());
-        len = UTF8SKIP(s);  /* Should be well-formed; so this is its length */
-        argsv = tmpsv;
-    }
-
     SETu(DO_UTF8(argsv)
            ? utf8n_to_uvchr(s, len, 0, UTF8_ALLOW_ANYUV)
            : (UV)(*s));
@@ -3760,22 +3683,6 @@ PP(pp_chr)
     *tmps = '\0';
     (void)SvPOK_only(TARG);
 
-    if (IN_ENCODING && !IN_BYTES) {
-        sv_recode_to_utf8(TARG, _get_encoding());
-       tmps = SvPVX(TARG);
-       if (SvCUR(TARG) == 0
-           || ! is_utf8_string((U8*)tmps, SvCUR(TARG))
-           || UTF8_IS_REPLACEMENT((U8*) tmps, (U8*) tmps + SvCUR(TARG)))
-       {
-           SvGROW(TARG, 2);
-           tmps = SvPVX(TARG);
-           SvCUR_set(TARG, 1);
-           *tmps++ = (char)value;
-           *tmps = '\0';
-           SvUTF8_off(TARG);
-       }
-    }
-
     SETTARG;
     return NORMAL;
 }
@@ -3865,10 +3772,7 @@ PP(pp_ucfirst)
     /* We may be able to get away with changing only the first character, in
      * place, but not if read-only, etc.  Later we may discover more reasons to
      * not convert in-place. */
-    inplace = !SvREADONLY(source)
-          && (  SvPADTMP(source)
-             || (  SvTEMP(source) && !SvSMAGICAL(source)
-                && SvREFCNT(source) == 1));
+    inplace = !SvREADONLY(source) && SvPADTMP(source);
 
     /* First calculate what the changed first character should be.  This affects
      * whether we can just swap it out, leaving the rest of the string unchanged,
@@ -4108,9 +4012,7 @@ PP(pp_uc)
 
     SvGETMAGIC(source);
 
-    if ((SvPADTMP(source)
-        ||
-       (SvTEMP(source) && !SvSMAGICAL(source) && SvREFCNT(source) == 1))
+    if (   SvPADTMP(source)
        && !SvREADONLY(source) && SvPOK(source)
        && !DO_UTF8(source)
        && (
@@ -4207,8 +4109,7 @@ PP(pp_uc)
                      * allocate without allocating too much.  Such is life.
                      * See corresponding comment in lc code for another option
                      * */
-                    SvGROW(dest, min);
-                    d = (U8*)SvPVX(dest) + o;
+                    d = o + (U8*) SvGROW(dest, min);
                 }
                 Copy(tmpbuf, d, ulen, U8);
                 d += ulen;
@@ -4272,8 +4173,7 @@ PP(pp_uc)
                         * ASCII.  If not enough room, grow the string */
                        if (SvLEN(dest) < ++min) {      
                            const UV o = d - (U8*)SvPVX_const(dest);
-                           SvGROW(dest, min);
-                           d = (U8*)SvPVX(dest) + o;
+                           d = o + (U8*) SvGROW(dest, min);
                        }
                        *d++ = 'S'; *d = 'S'; /* upper case is 'SS' */
                        continue;   /* Back to the tight loop; still in ASCII */
@@ -4367,10 +4267,7 @@ PP(pp_lc)
 
     SvGETMAGIC(source);
 
-    if (   (  SvPADTMP(source)
-          || (  SvTEMP(source) && !SvSMAGICAL(source)
-             && SvREFCNT(source) == 1  )
-          )
+    if (   SvPADTMP(source)
        && !SvREADONLY(source) && SvPOK(source)
        && !DO_UTF8(source)) {
 
@@ -4426,8 +4323,7 @@ PP(pp_lc)
                 * Another option would be to grow an extra byte or two more
                 * each time we need to grow, which would cut down the million
                 * to 500K, with little waste */
-               SvGROW(dest, min);
-               d = (U8*)SvPVX(dest) + o;
+               d = o + (U8*) SvGROW(dest, min);
            }
 
            /* Copy the newly lowercased letter to the output buffer we're
@@ -4621,8 +4517,7 @@ PP(pp_fc)
 
             if (ulen > u && (SvLEN(dest) < (min += ulen - u))) {
                 const UV o = d - (U8*)SvPVX_const(dest);
-                SvGROW(dest, min);
-                d = (U8*)SvPVX(dest) + o;
+                d = o + (U8*) SvGROW(dest, min);
             }
 
             Copy(tmpbuf, d, ulen, U8);
@@ -4701,8 +4596,7 @@ PP(pp_fc)
                      * becomes "ss", which may require growing the SV. */
                     if (SvLEN(dest) < ++min) {
                         const UV o = d - (U8*)SvPVX_const(dest);
-                        SvGROW(dest, min);
-                        d = (U8*)SvPVX(dest) + o;
+                        d = o + (U8*) SvGROW(dest, min);
                      }
                     *(d)++ = 's';
                     *d = 's';
@@ -4845,7 +4739,7 @@ PP(pp_aeach)
 {
     dSP;
     AV *array = MUTABLE_AV(POPs);
-    const I32 gimme = GIMME_V;
+    const U8 gimme = GIMME_V;
     IV *iterp = Perl_av_iter_p(aTHX_ array);
     const IV current = (*iterp)++;
 
@@ -4871,7 +4765,7 @@ PP(pp_akeys)
 {
     dSP;
     AV *array = MUTABLE_AV(POPs);
-    const I32 gimme = GIMME_V;
+    const U8 gimme = GIMME_V;
 
     *Perl_av_iter_p(aTHX_ array) = 0;
 
@@ -4880,12 +4774,23 @@ PP(pp_akeys)
        PUSHi(av_tindex(array) + 1);
     }
     else if (gimme == G_ARRAY) {
+      if (UNLIKELY(PL_op->op_private & OPpMAYBE_LVSUB)) {
+        const I32 flags = is_lvalue_sub();
+        if (flags && !(flags & OPpENTERSUB_INARGS))
+            /* diag_listed_as: Can't modify %s in %s */
+            Perl_croak(aTHX_
+                      "Can't modify keys on array in list assignment");
+      }
+      {
         IV n = Perl_av_len(aTHX_ array);
         IV i;
 
         EXTEND(SP, n + 1);
 
-       if (PL_op->op_type == OP_AKEYS) {
+       if (  PL_op->op_type == OP_AKEYS
+          || (  PL_op->op_type == OP_AVHVSWITCH
+             && (PL_op->op_private & 3) + OP_AEACH == OP_AKEYS  ))
+       {
            for (i = 0;  i <= n;  i++) {
                mPUSHi(i);
            }
@@ -4896,6 +4801,7 @@ PP(pp_akeys)
                PUSHs(elem ? *elem : &PL_sv_undef);
            }
        }
+      }
     }
     RETURN;
 }
@@ -4907,7 +4813,7 @@ PP(pp_each)
     dSP;
     HV * hash = MUTABLE_HV(POPs);
     HE *entry;
-    const I32 gimme = GIMME_V;
+    const U8 gimme = GIMME_V;
 
     entry = hv_iternext(hash);
 
@@ -4931,7 +4837,7 @@ STATIC OP *
 S_do_delete_local(pTHX)
 {
     dSP;
-    const I32 gimme = GIMME_V;
+    const U8 gimme = GIMME_V;
     const MAGIC *mg;
     HV *stash;
     const bool sliced = !!(PL_op->op_private & OPpSLICE);
@@ -5041,7 +4947,7 @@ S_do_delete_local(pTHX)
 PP(pp_delete)
 {
     dSP;
-    I32 gimme;
+    U8 gimme;
     I32 discard;
 
     if (PL_op->op_private & OPpLVAL_INTRO)
@@ -5206,7 +5112,8 @@ PP(pp_kvhslice)
        if (flags) {
            if (!(flags & OPpENTERSUB_INARGS))
                /* diag_listed_as: Can't modify %s in %s */
-              Perl_croak(aTHX_ "Can't modify key/value hash slice in list assignment");
+              Perl_croak(aTHX_ "Can't modify key/value hash slice in %s assignment",
+                                GIMME_V == G_ARRAY ? "list" : "scalar");
           lval = flags;
        }
     }
@@ -5355,41 +5262,11 @@ PP(pp_anonhash)
     RETURN;
 }
 
-static AV *
-S_deref_plain_array(pTHX_ AV *ary)
-{
-    if (SvTYPE(ary) == SVt_PVAV) return ary;
-    SvGETMAGIC((SV *)ary);
-    if (!SvROK(ary) || SvTYPE(SvRV(ary)) != SVt_PVAV)
-       Perl_die(aTHX_ "Not an ARRAY reference");
-    else if (SvOBJECT(SvRV(ary)))
-       Perl_die(aTHX_ "Not an unblessed ARRAY reference");
-    return (AV *)SvRV(ary);
-}
-
-#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN)
-# define DEREF_PLAIN_ARRAY(ary)       \
-   ({                                  \
-     AV *aRrRay = ary;                  \
-     SvTYPE(aRrRay) == SVt_PVAV          \
-      ? aRrRay                            \
-      : S_deref_plain_array(aTHX_ aRrRay); \
-   })
-#else
-# define DEREF_PLAIN_ARRAY(ary)            \
-   (                                        \
-     PL_Sv = (SV *)(ary),                    \
-     SvTYPE(PL_Sv) == SVt_PVAV                \
-      ? (AV *)PL_Sv                            \
-      : S_deref_plain_array(aTHX_ (AV *)PL_Sv)  \
-   )
-#endif
-
 PP(pp_splice)
 {
     dSP; dMARK; dORIGMARK;
     int num_args = (SP - MARK);
-    AV *ary = DEREF_PLAIN_ARRAY(MUTABLE_AV(*++MARK));
+    AV *ary = MUTABLE_AV(*++MARK);
     SV **src;
     SV **dst;
     SSize_t i;
@@ -5486,6 +5363,8 @@ PP(pp_splice)
                for (i = length - 1, dst = &AvARRAY(ary)[offset]; i > 0; i--)
                    SvREFCNT_dec(*dst++);       /* free them now */
            }
+           if (!*MARK)
+               *MARK = &PL_sv_undef;
        }
        AvFILLp(ary) += diff;
 
@@ -5582,6 +5461,8 @@ PP(pp_splice)
                while (length-- > 0)
                    SvREFCNT_dec(tmparyval[length]);
            }
+           if (!*MARK)
+               *MARK = &PL_sv_undef;
        }
        else
            *MARK = &PL_sv_undef;
@@ -5598,7 +5479,7 @@ PP(pp_splice)
 PP(pp_push)
 {
     dSP; dMARK; dORIGMARK; dTARGET;
-    AV * const ary = DEREF_PLAIN_ARRAY(MUTABLE_AV(*++MARK));
+    AV * const ary = MUTABLE_AV(*++MARK);
     const MAGIC * const mg = SvTIED_mg((const SV *)ary, PERL_MAGIC_tied);
 
     if (mg) {
@@ -5641,7 +5522,7 @@ PP(pp_shift)
 {
     dSP;
     AV * const av = PL_op->op_flags & OPf_SPECIAL
-       ? MUTABLE_AV(GvAV(PL_defgv)) : DEREF_PLAIN_ARRAY(MUTABLE_AV(POPs));
+       ? MUTABLE_AV(GvAVn(PL_defgv)) : MUTABLE_AV(POPs);
     SV * const sv = PL_op->op_type == OP_SHIFT ? av_shift(av) : av_pop(av);
     EXTEND(SP, 1);
     assert (sv);
@@ -5654,7 +5535,7 @@ PP(pp_shift)
 PP(pp_unshift)
 {
     dSP; dMARK; dORIGMARK; dTARGET;
-    AV *ary = DEREF_PLAIN_ARRAY(MUTABLE_AV(*++MARK));
+    AV *ary = MUTABLE_AV(*++MARK);
     const MAGIC * const mg = SvTIED_mg((const SV *)ary, PERL_MAGIC_tied);
 
     if (mg) {
@@ -5844,7 +5725,7 @@ PP(pp_split)
     const IV origlimit = limit;
     I32 realarray = 0;
     I32 base;
-    const I32 gimme = GIMME_V;
+    const U8 gimme = GIMME_V;
     bool gimme_scalar;
     const I32 oldsave = PL_savestack_ix;
     U32 make_mortal = SVs_TEMP;
@@ -6285,7 +6166,7 @@ PP(pp_lock)
 }
 
 
-/* used for: pp_padany(), pp_mapstart(), pp_custom(); plus any system ops
+/* used for: pp_padany(), pp_custom(); plus any system ops
  * that aren't implemented on a particular platform */
 
 PP(unimplemented_op)
@@ -6306,6 +6187,18 @@ PP(unimplemented_op)
     DIE(aTHX_ "panic: unimplemented op %s (#%d) called", name, op_type);
 }
 
+static void
+S_maybe_unwind_defav(pTHX)
+{
+    if (CX_CUR()->cx_type & CXp_HASARGS) {
+       PERL_CONTEXT *cx = CX_CUR();
+
+        assert(CxHASARGS(cx));
+        cx_popsub_args(cx);
+       cx->cx_type &= ~CXp_HASARGS;
+    }
+}
+
 /* For sorting out arguments passed to a &CORE:: subroutine */
 PP(pp_coreargs)
 {
@@ -6344,7 +6237,7 @@ PP(pp_coreargs)
        to return.  nextstate usually does this on sub entry, but we need
        to run the next op with the caller's hints, so we cannot have a
        nextstate. */
-    SP = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;
+    SP = PL_stack_base + CX_CUR()->blk_oldsp;
 
     if(!maxargs) RETURN;
 
@@ -6376,13 +6269,39 @@ PP(pp_coreargs)
                svp++;
            }
            RETURN;
+       case OA_AVREF:
+           if (!numargs) {
+               GV *gv;
+               if (CvUNIQUE(find_runcv_where(FIND_RUNCV_level_eq,1,NULL)))
+                   gv = PL_argvgv;
+               else {
+                   S_maybe_unwind_defav(aTHX);
+                   gv = PL_defgv;
+               }
+               PUSHs((SV *)GvAVn(gv));
+               break;
+           }
+           if (!svp || !*svp || !SvROK(*svp)
+            || SvTYPE(SvRV(*svp)) != SVt_PVAV)
+               DIE(aTHX_
+               /* diag_listed_as: Type of arg %d to &CORE::%s must be %s*/
+                "Type of arg %d to &CORE::%s must be array reference",
+                 whicharg, PL_op_desc[opnum]
+               );
+           PUSHs(SvRV(*svp));
+           break;
        case OA_HVREF:
            if (!svp || !*svp || !SvROK(*svp)
-            || SvTYPE(SvRV(*svp)) != SVt_PVHV)
+            || (  SvTYPE(SvRV(*svp)) != SVt_PVHV
+               && (  opnum == OP_DBMCLOSE || opnum == OP_DBMOPEN
+                  || SvTYPE(SvRV(*svp)) != SVt_PVAV  )))
                DIE(aTHX_
                /* diag_listed_as: Type of arg %d to &CORE::%s must be %s*/
-                "Type of arg %d to &CORE::%s must be hash reference",
-                 whicharg, OP_DESC(PL_op->op_next)
+                "Type of arg %d to &CORE::%s must be hash%s reference",
+                 whicharg, PL_op_desc[opnum],
+                 opnum == OP_DBMCLOSE || opnum == OP_DBMOPEN
+                    ? ""
+                    : " or array"
                );
            PUSHs(SvRV(*svp));
            break;
@@ -6427,14 +6346,10 @@ PP(pp_coreargs)
                       : "reference to one of [$@%*]"
                );
            PUSHs(SvRV(*svp));
-           if (opnum == OP_UNDEF && SvRV(*svp) == (SV *)PL_defgv
-            && cxstack[cxstack_ix].cx_type & CXp_HASARGS) {
+           if (opnum == OP_UNDEF && SvRV(*svp) == (SV *)PL_defgv) {
                /* Undo @_ localisation, so that sub exit does not undo
                   part of our undeffing. */
-               PERL_CONTEXT *cx = &cxstack[cxstack_ix];
-               POP_SAVEARRAY();
-               cx->cx_type &= ~ CXp_HASARGS;
-               assert(!AvREAL(cx->blk_sub.argarray));
+               S_maybe_unwind_defav(aTHX);
            }
          }
          break;
@@ -6447,6 +6362,15 @@ PP(pp_coreargs)
     RETURN;
 }
 
+PP(pp_avhvswitch)
+{
+    dVAR; dSP;
+    return PL_ppaddr[
+               (SvTYPE(TOPs) == SVt_PVAV ? OP_AEACH : OP_EACH)
+                   + (PL_op->op_private & 3)
+          ](aTHX);
+}
+
 PP(pp_runcv)
 {
     dSP;
@@ -6683,6 +6607,226 @@ PP(pp_anonconst)
     RETURN;
 }
 
+
+/* process one subroutine argument - typically when the sub has a signature:
+ * introduce PL_curpad[op_targ] and assign to it the value
+ *  for $:   (OPf_STACKED ? *sp : $_[N])
+ *  for @/%: @_[N..$#_]
+ *
+ * It's equivalent to 
+ *    my $foo = $_[N];
+ * or
+ *    my $foo = (value-on-stack)
+ * or
+ *    my @foo = @_[N..$#_]
+ * etc
+ */
+
+PP(pp_argelem)
+{
+    dTARG;
+    SV *val;
+    SV ** padentry;
+    OP *o = PL_op;
+    AV *defav = GvAV(PL_defgv); /* @_ */
+    IV ix = PTR2IV(cUNOP_AUXo->op_aux);
+    IV argc;
+
+    /* do 'my $var, @var or %var' action */
+    padentry = &(PAD_SVl(o->op_targ));
+    save_clearsv(padentry);
+    targ = *padentry;
+
+    if ((o->op_private & OPpARGELEM_MASK) == OPpARGELEM_SV) {
+        if (o->op_flags & OPf_STACKED) {
+            dSP;
+            val = POPs;
+            PUTBACK;
+        }
+        else {
+            SV **svp;
+            /* should already have been checked */
+            assert(ix >= 0);
+#if IVSIZE > PTRSIZE
+            assert(ix <= SSize_t_MAX);
+#endif
+
+            svp = av_fetch(defav, ix, FALSE);
+            val = svp ? *svp : &PL_sv_undef;
+        }
+
+        /* $var = $val */
+
+        /* cargo-culted from pp_sassign */
+        assert(TAINTING_get || !TAINT_get);
+        if (UNLIKELY(TAINT_get) && !SvTAINTED(val))
+            TAINT_NOT;
+
+        SvSetMagicSV(targ, val);
+        return o->op_next;
+    }
+
+    /* must be AV or HV */
+
+    assert(!(o->op_flags & OPf_STACKED));
+    argc = ((IV)AvFILL(defav) + 1) - ix;
+
+    /* This is a copy of the relevant parts of pp_aassign().
+     */
+    if ((o->op_private & OPpARGELEM_MASK) == OPpARGELEM_AV) {
+        IV i;
+
+        if (AvFILL((AV*)targ) > -1) {
+            /* target should usually be empty. If we get get
+             * here, someone's been doing some weird closure tricks.
+             * Make a copy of all args before clearing the array,
+             * to avoid the equivalent of @a = ($a[0]) prematurely freeing
+             * elements. See similar code in pp_aassign.
+             */
+            for (i = 0; i < argc; i++) {
+                SV **svp = av_fetch(defav, ix + i, FALSE);
+                SV *newsv = newSV(0);
+                sv_setsv_flags(newsv,
+                                svp ? *svp : &PL_sv_undef,
+                                (SV_DO_COW_SVSETSV|SV_NOSTEAL));
+                if (!av_store(defav, ix + i, newsv))
+                    SvREFCNT_dec_NN(newsv);
+            }
+            av_clear((AV*)targ);
+        }
+
+        if (argc <= 0)
+            return o->op_next;
+
+        av_extend((AV*)targ, argc);
+
+        i = 0;
+        while (argc--) {
+            SV *tmpsv;
+            SV **svp = av_fetch(defav, ix + i, FALSE);
+            SV *val = svp ? *svp : &PL_sv_undef;
+            tmpsv = newSV(0);
+            sv_setsv(tmpsv, val);
+            av_store((AV*)targ, i++, tmpsv);
+            TAINT_NOT;
+        }
+
+    }
+    else {
+        IV i;
+
+        assert((o->op_private & OPpARGELEM_MASK) == OPpARGELEM_HV);
+
+        if (SvRMAGICAL(targ) || HvUSEDKEYS((HV*)targ)) {
+            /* see "target should usually be empty" comment above */
+            for (i = 0; i < argc; i++) {
+                SV **svp = av_fetch(defav, ix + i, FALSE);
+                SV *newsv = newSV(0);
+                sv_setsv_flags(newsv,
+                                svp ? *svp : &PL_sv_undef,
+                                (SV_DO_COW_SVSETSV|SV_NOSTEAL));
+                if (!av_store(defav, ix + i, newsv))
+                    SvREFCNT_dec_NN(newsv);
+            }
+            hv_clear((HV*)targ);
+        }
+
+        if (argc <= 0)
+            return o->op_next;
+        assert(argc % 2 == 0);
+
+        i = 0;
+        while (argc) {
+            SV *tmpsv;
+            SV **svp;
+            SV *key;
+            SV *val;
+
+            svp = av_fetch(defav, ix + i++, FALSE);
+            key = svp ? *svp : &PL_sv_undef;
+            svp = av_fetch(defav, ix + i++, FALSE);
+            val = svp ? *svp : &PL_sv_undef;
+
+            argc -= 2;
+            if (UNLIKELY(SvGMAGICAL(key)))
+                key = sv_mortalcopy(key);
+            tmpsv = newSV(0);
+            sv_setsv(tmpsv, val);
+            hv_store_ent((HV*)targ, key, tmpsv, 0);
+            TAINT_NOT;
+        }
+    }
+
+    return o->op_next;
+}
+
+/* Handle a default value for one subroutine argument (typically as part
+ * of a subroutine signature).
+ * It's equivalent to
+ *    @_ > op_targ ? $_[op_targ] : result_of(op_other)
+ *
+ * Intended to be used where op_next is an OP_ARGELEM
+ *
+ * We abuse the op_targ field slightly: it's an index into @_ rather than
+ * into PL_curpad.
+ */
+
+PP(pp_argdefelem)
+{
+    OP * const o = PL_op;
+    AV *defav = GvAV(PL_defgv); /* @_ */
+    IV ix = (IV)o->op_targ;
+
+    assert(ix >= 0);
+#if IVSIZE > PTRSIZE
+    assert(ix <= SSize_t_MAX);
+#endif
+
+    if (AvFILL(defav) >= ix) {
+        dSP;
+        SV **svp = av_fetch(defav, ix, FALSE);
+        SV  *val = svp ? *svp : &PL_sv_undef;
+        XPUSHs(val);
+        RETURN;
+    }
+    return cLOGOPo->op_other;
+}
+
+
+
+/* Check a  a subs arguments - i.e. that it has the correct number of args
+ * (and anything else we might think of in future). Typically used with
+ * signatured subs.
+ */
+
+PP(pp_argcheck)
+{
+    OP * const o       = PL_op;
+    UNOP_AUX_item *aux = cUNOP_AUXo->op_aux;
+    IV   params        = aux[0].iv;
+    IV   opt_params    = aux[1].iv;
+    char slurpy        = (char)(aux[2].iv);
+    AV  *defav         = GvAV(PL_defgv); /* @_ */
+    IV   argc;
+    bool too_few;
+
+    assert(!SvMAGICAL(defav));
+    argc = (AvFILLp(defav) + 1);
+    too_few = (argc < (params - opt_params));
+
+    if (UNLIKELY(too_few || (!slurpy && argc > params)))
+        /* diag_listed_as: Too few arguments for subroutine */
+        /* diag_listed_as: Too many arguments for subroutine */
+        Perl_croak_caller("Too %s arguments for subroutine",
+                            too_few ? "few" : "many");
+
+    if (UNLIKELY(slurpy == '%' && argc > params && (argc - params) % 2))
+        Perl_croak_caller("Odd name/value argument for subroutine");
+
+
+    return NORMAL;
+}
+
 /*
  * ex: set ts=8 sts=4 sw=4 et:
  */