]> git.vpit.fr Git - perl/modules/autovivification.git/blobdiff - autovivification.xs
Sanitize the return value of a_do_multideref()
[perl/modules/autovivification.git] / autovivification.xs
index b9843fb38ebc08628975f29c20dc75685e595300..0d7e5ed34e1b0257f1e6aefb1bcb32a7d47b63e6 100644 (file)
@@ -800,6 +800,31 @@ static OP *a_pp_root_binop(pTHX) {
 
 /* ... pp_multideref ....................................................... */
 
+static UV a_do_multideref(const OP *o, UV flags) {
+ UV isexdel, other_flags;
+
+ assert(o->op_type == OP_MULTIDEREF);
+
+ other_flags = flags & ~A_HINT_DO;
+
+ isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE);
+ if (isexdel) {
+  if (isexdel & OPpMULTIDEREF_EXISTS) {
+   flags &= A_HINT_EXISTS;
+  } else {
+   flags &= A_HINT_DELETE;
+  }
+ } else {
+  if (o->op_flags & OPf_MOD) {
+   flags &= A_HINT_STORE;
+  } else {
+   flags &= A_HINT_FETCH;
+  }
+ }
+
+ return flags ? (flags | other_flags) : 0;
+}
+
 static SV *a_do_fake_pp(pTHX_ OP *op) {
 #define a_do_fake_pp(O) a_do_fake_pp(aTHX_ (O))
  {
@@ -841,7 +866,7 @@ static SV *a_do_fake_pp_unop_arg1(pTHX_ U32 type, U32 flags, SV *arg) {
  PUSHs(arg);
  PUTBACK;
 
- return a_do_fake_pp(&unop);
+ return a_do_fake_pp((OP *) &unop);
 }
 
 static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) {
@@ -856,46 +881,40 @@ static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2)
  PUSHs(arg2);
  PUTBACK;
 
- return a_do_fake_pp(&unop);
+ return a_do_fake_pp((OP *) &unop);
 }
 
-#define a_do_pp_rv2av(R)      a_do_fake_pp_unop_arg1(OP_RV2AV,  OPf_REF, (R))
-#define a_do_pp_aexists(A, I) a_do_fake_pp_unop_arg2(OP_EXISTS, OPf_SPECIAL, (A), (I))
-#define a_do_pp_adelete(A, I) a_do_fake_pp_unop_arg2(OP_DELETE, OPf_SPECIAL, (A), (I))
-#define a_do_pp_aelem(A, I)   a_do_fake_pp_unop_arg2(OP_AELEM,  0,  (A), (I))
+#define a_do_pp_rv2av(R)        a_do_fake_pp_unop_arg1(OP_RV2AV,  OPf_REF,     (R))
+#define a_do_pp_afetch(A, I)    a_do_fake_pp_unop_arg2(OP_AELEM,  0,           (A), (I))
+#define a_do_pp_afetch_lv(A, I) a_do_fake_pp_unop_arg2(OP_AELEM,  OPf_MOD,     (A), (I))
+#define a_do_pp_aexists(A, I)   a_do_fake_pp_unop_arg2(OP_EXISTS, OPf_SPECIAL, (A), (I))
+#define a_do_pp_adelete(A, I)   a_do_fake_pp_unop_arg2(OP_DELETE, OPf_SPECIAL, (A), (I))
 
-#define a_do_pp_rv2hv(R)      a_do_fake_pp_unop_arg1(OP_RV2HV,  OPf_REF, (R))
-#define a_do_pp_hexists(H, K) a_do_fake_pp_unop_arg2(OP_EXISTS, 0,  (H), (K))
-#define a_do_pp_hdelete(H, K) a_do_fake_pp_unop_arg2(OP_DELETE, 0,  (H), (K))
-#define a_do_pp_helem(H, K)   a_do_fake_pp_unop_arg2(OP_HELEM,  0,  (H), (K))
+#define a_do_pp_rv2hv(R)        a_do_fake_pp_unop_arg1(OP_RV2HV,  OPf_REF, (R))
+#define a_do_pp_hfetch(H, K)    a_do_fake_pp_unop_arg2(OP_HELEM,  0,       (H), (K))
+#define a_do_pp_hfetch_lv(H, K) a_do_fake_pp_unop_arg2(OP_HELEM,  OPf_MOD, (H), (K))
+#define a_do_pp_hexists(H, K)   a_do_fake_pp_unop_arg2(OP_EXISTS, 0,  (H), (K))
+#define a_do_pp_hdelete(H, K)   a_do_fake_pp_unop_arg2(OP_DELETE, 0,  (H), (K))
 
 static OP *a_pp_multideref(pTHX) {
- UNOP_AUX_item *items = cUNOP_AUXx(PL_op)->op_aux;
- UV  actions = items->uv;
- U8  isexdel = PL_op->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE);
- UV  flags   = 0;
- SV *sv      = NULL;
+ UNOP_AUX_item *items;
+ UV  actions;
+ UV  flags = 0;
+ SV *sv    = NULL;
  dSP;
 
  {
   dA_MAP_THX;
   const a_op_info *oi = a_map_fetch(PL_op);
-  flags = oi->flags;
-  if (isexdel) {
-   if (isexdel & OPpMULTIDEREF_EXISTS) {
-    if (flags & A_HINT_EXISTS)
-     goto hijack;
-   } else if (flags & A_HINT_DELETE) {
-    assert(isexdel & OPpMULTIDEREF_DELETE);
-    goto hijack;
-   }
-  } else if (flags & A_HINT_FETCH) {
-   goto hijack;
-  }
-  return oi->old_pp(aTHX);
+  assert(oi);
+  flags = a_do_multideref(PL_op, oi->flags);
+  if (!flags)
+   return oi->old_pp(aTHX);
  }
 
-hijack:
+ items   = cUNOP_AUXx(PL_op)->op_aux;
+ actions = items->uv;
+
  PL_multideref_pc = items;
 
  while (1) {
@@ -929,10 +948,10 @@ hijack:
     goto do_AV_vivify_rv2av_aelem;
    case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */
     sv = PAD_SVl((++items)->pad_offset);
-    if (a_undef(sv))
-     goto ret_undef;
     /* FALLTHROUGH */
    case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */
+    if (a_undef(sv))
+     goto ret_undef;
 do_AV_vivify_rv2av_aelem:
     sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_AV);
 do_AV_rv2av_aelem:
@@ -963,18 +982,24 @@ check_elem:
      }
      PL_multideref_pc = items;
      if (actions & MDEREF_FLAG_last) {
-      if (isexdel) {
-       if (isexdel & OPpMULTIDEREF_EXISTS)
+      switch (flags & A_HINT_DO) {
+       case A_HINT_FETCH:
+        sv = a_do_pp_afetch(sv, esv);
+        break;
+       case A_HINT_STORE:
+        sv = a_do_pp_afetch_lv(sv, esv);
+        break;
+       case A_HINT_EXISTS:
         sv = a_do_pp_aexists(sv, esv);
-       else
+        break;
+       case A_HINT_DELETE:
         sv = a_do_pp_adelete(sv, esv);
-      } else {
-       sv = a_do_pp_aelem(sv, esv);
+        break;
       }
       goto finish;
-     } else {
-      sv = a_do_pp_aelem(sv, esv);
      }
+     sv = a_do_pp_afetch(sv, esv);
+     break;
     }
    case MDEREF_HV_padhv_helem: /* $lex{...} */
     sv = PAD_SVl((++items)->pad_offset);
@@ -1002,10 +1027,10 @@ check_elem:
     goto do_HV_vivify_rv2hv_helem;
    case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */
     sv = PAD_SVl((++items)->pad_offset);
-    if (a_undef(sv))
-     goto ret_undef;
     /* FALLTHROUGH */
    case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */
+    if (a_undef(sv))
+     goto ret_undef;
 do_HV_vivify_rv2hv_helem:
     sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_HV);
 do_HV_rv2hv_helem:
@@ -1031,18 +1056,26 @@ do_HV_helem:
      }
      PL_multideref_pc = items;
      if (actions & MDEREF_FLAG_last) {
-      if (isexdel) {
-       if (isexdel & OPpMULTIDEREF_EXISTS)
+      switch (flags & A_HINT_DO) {
+       case A_HINT_FETCH:
+        sv = a_do_pp_hfetch(sv, key);
+        break;
+       case A_HINT_STORE:
+        sv = a_do_pp_hfetch_lv(sv, key);
+       break;
+       case A_HINT_EXISTS:
         sv = a_do_pp_hexists(sv, key);
-       else
+        break;
+       case A_HINT_DELETE:
         sv = a_do_pp_hdelete(sv, key);
-      } else {
-       sv = a_do_pp_helem(sv, key);
+        break;
+       default:
+        break;
       }
       goto finish;
-     } else {
-      sv = a_do_pp_helem(sv, key);
      }
+     sv = a_do_pp_hfetch(sv, key);
+     break;
     }
   }
 
@@ -1052,7 +1085,7 @@ do_HV_helem:
 ret_undef:
  if (flags & (A_HINT_NOTIFY|A_HINT_STORE))
   a_cannot_vivify(flags);
- if (isexdel & OPpMULTIDEREF_EXISTS)
+ if (flags & A_HINT_EXISTS)
   sv = &PL_sv_no;
  else
   sv = &PL_sv_undef;
@@ -1366,16 +1399,12 @@ static void a_peep_rec(pTHX_ OP *o, ptable *seen) {
 #if A_HAS_MULTIDEREF
    case OP_MULTIDEREF:
     if (o->op_ppaddr != a_pp_multideref) {
-     UV isexdel;
      oi = a_map_fetch(cUNOPo->op_first);
      if (!oi)
       break;
-     flags   = oi->flags;
-     isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE);
-     if (  ((flags & A_HINT_FETCH)  && !isexdel)
-         || (flags & A_HINT_EXISTS) && (isexdel & OPpMULTIDEREF_EXISTS)
-         || (flags & A_HINT_DELETE) && (isexdel & OPpMULTIDEREF_DELETE)) {
-      a_map_store(o, o->op_ppaddr, oi->next, oi->flags);
+     flags = oi->flags;
+     if (a_do_multideref(o, flags)) {
+      a_map_store_root(o, o->op_ppaddr, flags & ~A_HINT_DEREF);
       o->op_ppaddr = a_pp_multideref;
      }
     }