]> git.vpit.fr Git - perl/modules/autovivification.git/blobdiff - autovivification.xs
Reset the original pp whenever possible in pp_rv2xv
[perl/modules/autovivification.git] / autovivification.xs
index b6b3c3389dc11d397d23f201c2c25d734b39cf7e..8933e7a6e6ffcb1933199af528b4a75a3b4d34bf 100644 (file)
@@ -176,11 +176,7 @@ STATIC const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) {
 #endif
 
  val = ptable_fetch(a_op_map, o);
- if (val) {
-  *oi = *val;
-  val = oi;
- } else
-  oi->old_pp = 0;
+ *oi = *val;
 
 #ifdef USE_ITHREADS
  MUTEX_UNLOCK(&a_op_map_mutex);
@@ -217,12 +213,28 @@ STATIC void a_map_set_root(const OP *root, UV flags) {
   }
   if (!(o->op_flags & OPf_KIDS))
    break;
-  o = cUNOPo->op_first;
+  switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
+   case OA_BASEOP:
+   case OA_UNOP:
+   case OA_BINOP:
+   case OA_BASEOP_OR_UNOP:
+    o = cUNOPo->op_first;
+    break;
+   case OA_LIST:
+   case OA_LISTOP:
+    o = cLISTOPo->op_last;
+    break;
+   default:
+    goto done;
+  }
  }
 
+done:
 #ifdef USE_ITHREADS
  MUTEX_UNLOCK(&a_op_map_mutex);
 #endif
+
+ return;
 }
 
 /* ... Lightweight pp_defined() ............................................ */
@@ -258,7 +270,11 @@ STATIC OP *a_pp_rv2av(pTHX) {
  UV hint;
  dSP;
 
- if (!SvOK(TOPs)) {
+ a_map_fetch(PL_op, &oi);
+
+ if (PL_op == oi.root) { /* This means "@$arrayref" */
+  PL_op->op_ppaddr = oi.old_pp;
+ } else if (!SvOK(TOPs)) {
   /* We always need to push an empty array to fool the pp_aelem() that comes
    * later. */
   SV *av;
@@ -268,8 +284,6 @@ STATIC OP *a_pp_rv2av(pTHX) {
   RETURN;
  }
 
- a_map_fetch(PL_op, &oi);
-
  return CALL_FPTR(oi.old_pp)(aTHX);
 }
 
@@ -282,7 +296,9 @@ STATIC OP *a_pp_rv2hv(pTHX) {
 
  a_map_fetch(PL_op, &oi);
 
- if (!SvOK(TOPs)) {
+ if (PL_op == oi.root) { /* This means "%$hashref" */
+  PL_op->op_ppaddr = oi.old_pp;
+ } else if (!SvOK(TOPs)) {
   if (oi.root->op_flags & OPf_MOD) {
    SV *hv;
    POPs;
@@ -297,9 +313,6 @@ STATIC OP *a_pp_rv2hv(pTHX) {
 
 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
 
-STATIC const char a_msg_forbidden[]  = "Reference vivification forbidden";
-STATIC const char a_msg_impossible[] = "Can't vivify reference";
-
 STATIC OP *a_pp_deref(pTHX) {
  a_op_info oi;
  UV flags;
@@ -322,11 +335,11 @@ deref:
    SPAGAIN;
    if (!SvOK(TOPs)) {
     if (flags & A_HINT_STRICT)
-     croak(a_msg_forbidden);
+     croak("Reference vivification forbidden");
     else if (flags & A_HINT_WARN)
-      warn(a_msg_forbidden);
+      warn("Reference was vivified");
     else /* A_HINT_STORE */
-     croak(a_msg_impossible);
+     croak("Can't vivify reference");
    }
   }
 
@@ -362,9 +375,28 @@ deref:
  return CALL_FPTR(oi.old_pp)(aTHX);
 }
 
-/* ... pp_root (exists,delete) ............................................. */
+/* ... pp_root (exists,delete,keys,values) ................................. */
 
-STATIC OP *a_pp_root(pTHX) {
+STATIC OP *a_pp_root_unop(pTHX) {
+ a_op_info oi;
+ dSP;
+
+ if (!a_defined(TOPs)) {
+  POPs;
+  /* Can only be reached by keys or values */
+  if (GIMME_V == G_SCALAR) {
+   dTARGET;
+   PUSHi(0);
+  }
+  RETURN;
+ }
+
+ a_map_fetch(PL_op, &oi);
+
+ return CALL_FPTR(oi.old_pp)(aTHX);
+}
+
+STATIC OP *a_pp_root_binop(pTHX) {
  a_op_info oi;
  dSP;
 
@@ -472,19 +504,6 @@ STATIC OP *a_ck_deref(pTHX_ OP *o) {
 
  hint = a_hint();
  if (hint & A_HINT_DO) {
-  if (!(hint & A_HINT_STRICT) && o->op_flags & OPf_KIDS) {
-   OP *kid = cUNOPo->op_first;
-   switch (kid->op_type) {
-    case OP_RV2AV:
-     a_map_store(kid, kid->op_ppaddr, hint);
-     kid->op_ppaddr = a_pp_rv2av;
-     break;
-    case OP_RV2HV:
-     a_map_store(kid, kid->op_ppaddr, hint);
-     kid->op_ppaddr = a_pp_rv2hv;
-     break;
-   }
-  }
   a_map_store(o, o->op_ppaddr, hint);
   o->op_ppaddr = a_pp_deref;
   a_map_set_root(o, hint);
@@ -494,35 +513,85 @@ STATIC OP *a_ck_deref(pTHX_ OP *o) {
  return o;
 }
 
-/* ... ck_root (exists,delete) ............................................. */
+/* ... ck_rv2xv (rv2av,rv2hv) .............................................. */
+
+STATIC OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0;
+
+STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
+ OP * (*old_ck)(pTHX_ OP *o) = 0;
+ OP * (*new_pp)(pTHX)        = 0;
+ UV hint;
+
+ switch (o->op_type) {
+  case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
+  case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv; break;
+ }
+ o = CALL_FPTR(old_ck)(aTHX_ o);
+
+ if (cUNOPo->op_first->op_type == OP_GV)
+  return o;
+
+ hint = a_hint();
+ if (hint & A_HINT_DO) {
+  if (!(hint & A_HINT_STRICT)) {
+   a_map_store(o, o->op_ppaddr, hint);
+   o->op_ppaddr = new_pp;
+  }
+  a_map_set_root(o, hint);
+ } else
+  a_map_delete(o);
+
+ return o;
+}
+
+/* ... ck_root (exists,delete,keys,values) ................................. */
 
 STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
 STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_keys)  (pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
 
 STATIC OP *a_ck_root(pTHX_ OP *o) {
  OP * (*old_ck)(pTHX_ OP *o) = 0;
+ OP * (*new_pp)(pTHX)        = 0;
  bool enabled = FALSE;
  UV hint = a_hint();
 
  switch (o->op_type) {
   case OP_EXISTS:
    old_ck  = a_old_ck_exists;
+   new_pp  = a_pp_root_binop;
    enabled = hint & A_HINT_EXISTS;
    break;
   case OP_DELETE:
    old_ck  = a_old_ck_delete;
+   new_pp  = a_pp_root_binop;
    enabled = hint & A_HINT_DELETE;
    break;
+  case OP_KEYS:
+   old_ck  = a_old_ck_keys;
+   new_pp  = a_pp_root_unop;
+   enabled = hint & A_HINT_FETCH;
+   break;
+  case OP_VALUES:
+   old_ck  = a_old_ck_values;
+   new_pp  = a_pp_root_unop;
+   enabled = hint & A_HINT_FETCH;
+   break;
  }
  o = CALL_FPTR(old_ck)(aTHX_ o);
 
- if (enabled) {
-  a_map_set_root(o, hint | A_HINT_DEREF);
-  a_map_store(o, o->op_ppaddr, hint);
-  o->op_ppaddr = a_pp_root;
- } else {
-  a_map_set_root(o, 0);
- }
+ if (hint & A_HINT_DO) {
+  if (enabled) {
+   a_map_set_root(o, hint | A_HINT_DEREF);
+   a_map_store(o, o->op_ppaddr, hint);
+   o->op_ppaddr = new_pp;
+  } else {
+   a_map_set_root(o, 0);
+  }
+ } else
+  a_map_delete(o);
 
  return o;
 }
@@ -551,16 +620,27 @@ BOOT:
   PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany);
   a_old_ck_padsv      = PL_check[OP_PADSV];
   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(a_ck_padsv);
+
   a_old_ck_aelem      = PL_check[OP_AELEM];
   PL_check[OP_AELEM]  = MEMBER_TO_FPTR(a_ck_deref);
   a_old_ck_helem      = PL_check[OP_HELEM];
   PL_check[OP_HELEM]  = MEMBER_TO_FPTR(a_ck_deref);
   a_old_ck_rv2sv      = PL_check[OP_RV2SV];
   PL_check[OP_RV2SV]  = MEMBER_TO_FPTR(a_ck_deref);
+
+  a_old_ck_rv2av      = PL_check[OP_RV2AV];
+  PL_check[OP_RV2AV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
+  a_old_ck_rv2hv      = PL_check[OP_RV2HV];
+  PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
+
   a_old_ck_exists     = PL_check[OP_EXISTS];
   PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
   a_old_ck_delete     = PL_check[OP_DELETE];
   PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root);
+  a_old_ck_keys       = PL_check[OP_KEYS];
+  PL_check[OP_KEYS]   = MEMBER_TO_FPTR(a_ck_root);
+  a_old_ck_values     = PL_check[OP_VALUES];
+  PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root);
 
   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
   newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));