]> git.vpit.fr Git - perl/modules/autovivification.git/blob - autovivification.xs
Make sure a_defined() calls get magic before testing for definedness
[perl/modules/autovivification.git] / autovivification.xs
1 /* This file is part of the autovivification Perl module.
2  * See http://search.cpan.org/dist/autovivification/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "autovivification"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #define A_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15
16 #ifndef A_WORKAROUND_REQUIRE_PROPAGATION
17 # define A_WORKAROUND_REQUIRE_PROPAGATION !A_HAS_PERL(5, 10, 1)
18 #endif
19
20 /* --- Helpers ------------------------------------------------------------- */
21
22 #if A_WORKAROUND_REQUIRE_PROPAGATION
23
24 #define A_ENCODE_UV(B, U)   \
25  len = 0;                   \
26  while (len < sizeof(UV)) { \
27   (B)[len++] = (U) & 0xFF;  \
28   (U) >>= 8;                \
29  }
30
31 #define A_DECODE_UV(U, B)        \
32  len = sizeof(UV);               \
33  while (len > 0)                 \
34   (U) = ((U) << 8) | (B)[--len];
35
36 #if A_WORKAROUND_REQUIRE_PROPAGATION
37 STATIC UV a_require_tag(pTHX) {
38 #define a_require_tag() a_require_tag(aTHX)
39  const PERL_SI *si;
40
41  for (si = PL_curstackinfo; si; si = si->si_prev) {
42   I32 cxix;
43
44   for (cxix = si->si_cxix; cxix >= 0; --cxix) {
45    const PERL_CONTEXT *cx = si->si_cxstack + cxix;
46
47    if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
48     return PTR2UV(cx);
49   }
50  }
51
52  return PTR2UV(NULL);
53 }
54 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
55
56 STATIC SV *a_tag(pTHX_ UV bits) {
57 #define a_tag(B) a_tag(aTHX_ (B))
58  SV            *hint;
59  const PERL_SI *si;
60  UV             cxreq;
61  unsigned char  buf[sizeof(UV) * 2];
62  STRLEN         len;
63
64  cxreq = a_require_tag();
65  A_ENCODE_UV(buf,              cxreq);
66  A_ENCODE_UV(buf + sizeof(UV), bits);
67  hint = newSVpvn(buf, sizeof buf);
68  SvREADONLY_on(hint);
69
70  return hint;
71 }
72
73 STATIC UV a_detag(pTHX_ const SV *hint) {
74 #define a_detag(H) a_detag(aTHX_ (H))
75  const PERL_SI *si;
76  UV             cxreq = 0, bits = 0;
77  unsigned char *buf;
78  STRLEN         len;
79
80  if (!(hint && SvOK(hint)))
81   return 0;
82
83  buf = SvPVX(hint);
84
85  A_DECODE_UV(cxreq, buf);
86  if (a_require_tag() != cxreq)
87   return 0;
88
89  A_DECODE_UV(bits,  buf + sizeof(UV));
90
91  return bits;
92 }
93
94 #else /* A_WORKAROUND_REQUIRE_PROPAGATION */
95
96 #define a_tag(B)   newSVuv(B)
97 /* PVs fetched from the hints chain have their SvLEN set to zero, so get the UV
98  * from a copy. */
99 #define a_detag(H) \
100  ((H)              \
101   ? (SvIOK(H)      \
102      ? SvUVX(H)    \
103      : (SvPOK(H)   \
104         ? sv_2uv(SvLEN(H) ? (H) : sv_mortalcopy(H)) \
105         : 0        \
106        )           \
107      )             \
108   : 0)
109
110 #endif /* !A_WORKAROUND_REQUIRE_PROPAGATION */
111
112 /* Used both for hints and op flags */
113 #define A_HINT_STRICT 1
114 #define A_HINT_WARN   2
115 #define A_HINT_FETCH  4
116 #define A_HINT_STORE  8
117 #define A_HINT_EXISTS 16
118 #define A_HINT_DELETE 32
119 #define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN)
120 #define A_HINT_DO     (A_HINT_FETCH|A_HINT_STORE|A_HINT_EXISTS|A_HINT_DELETE)
121 #define A_HINT_MASK   (A_HINT_NOTIFY|A_HINT_DO)
122
123 /* Only used in op flags */
124 #define A_HINT_ROOT   64
125 #define A_HINT_DEREF  128
126
127 STATIC U32 a_hash = 0;
128
129 STATIC UV a_hint(pTHX) {
130 #define a_hint() a_hint(aTHX)
131  SV *hint;
132 #if A_HAS_PERL(5, 9, 5)
133  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
134                                        NULL,
135                                        __PACKAGE__, __PACKAGE_LEN__,
136                                        0,
137                                        a_hash);
138 #else
139  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, a_hash);
140  if (!val)
141   return 0;
142  hint = *val;
143 #endif
144  return a_detag(hint);
145 }
146
147 /* ... op => info map ...................................................... */
148
149 typedef struct {
150  OP *(*old_pp)(pTHX);
151  UV flags;
152  void *next;
153 } a_op_info;
154
155 #define PTABLE_NAME        ptable_map
156 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
157
158 #include "ptable.h"
159
160 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
161 #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
162
163 STATIC ptable *a_op_map = NULL;
164
165 #ifdef USE_ITHREADS
166 STATIC perl_mutex a_op_map_mutex;
167 #endif
168
169 STATIC const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) {
170  const a_op_info *val;
171
172 #ifdef USE_ITHREADS
173  MUTEX_LOCK(&a_op_map_mutex);
174 #endif
175
176  val = ptable_fetch(a_op_map, o);
177  if (val) {
178   *oi = *val;
179   val = oi;
180  }
181
182 #ifdef USE_ITHREADS
183  MUTEX_UNLOCK(&a_op_map_mutex);
184 #endif
185
186  return val;
187 }
188
189 STATIC const a_op_info *a_map_store_locked(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
190 #define a_map_store_locked(O, PP, N, F) a_map_store_locked(aPTBLMS_ (O), (PP), (N), (F))
191  a_op_info *oi;
192
193  if (!(oi = ptable_fetch(a_op_map, o))) {
194   oi = PerlMemShared_malloc(sizeof *oi);
195   ptable_map_store(a_op_map, o, oi);
196  }
197
198  oi->old_pp = old_pp;
199  oi->next   = next;
200  oi->flags  = flags;
201
202  return oi;
203 }
204
205 STATIC void a_map_store(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
206 #define a_map_store(O, PP, N, F) a_map_store(aPTBLMS_ (O), (PP), (N), (F))
207
208 #ifdef USE_ITHREADS
209  MUTEX_LOCK(&a_op_map_mutex);
210 #endif
211
212  a_map_store_locked(o, old_pp, next, flags);
213
214 #ifdef USE_ITHREADS
215  MUTEX_UNLOCK(&a_op_map_mutex);
216 #endif
217 }
218
219 STATIC void a_map_delete(pTHX_ const OP *o) {
220 #define a_map_delete(O) a_map_delete(aTHX_ (O))
221 #ifdef USE_ITHREADS
222  MUTEX_LOCK(&a_op_map_mutex);
223 #endif
224
225  ptable_map_store(a_op_map, o, NULL);
226
227 #ifdef USE_ITHREADS
228  MUTEX_UNLOCK(&a_op_map_mutex);
229 #endif
230 }
231
232 STATIC const OP *a_map_descend(const OP *o) {
233  switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
234   case OA_BASEOP:
235   case OA_UNOP:
236   case OA_BINOP:
237   case OA_BASEOP_OR_UNOP:
238    return cUNOPo->op_first;
239   case OA_LIST:
240   case OA_LISTOP:
241    return cLISTOPo->op_last;
242  }
243
244  return NULL;
245 }
246
247 STATIC void a_map_store_root(pPTBLMS_ const OP *root, OP *(*old_pp)(pTHX), UV flags) {
248 #define a_map_store_root(R, PP, F) a_map_store_root(aPTBLMS_ (R), (PP), (F))
249  const a_op_info *roi;
250  a_op_info *oi;
251  const OP *o = root;
252
253 #ifdef USE_ITHREADS
254  MUTEX_LOCK(&a_op_map_mutex);
255 #endif
256
257  roi = a_map_store_locked(o, old_pp, (OP *) root, flags | A_HINT_ROOT);
258
259  while (o->op_flags & OPf_KIDS) {
260   o = a_map_descend(o);
261   if (!o)
262    break;
263   if ((oi = ptable_fetch(a_op_map, o))) {
264    oi->flags &= ~A_HINT_ROOT;
265    oi->next   = (a_op_info *) roi;
266    break;
267   }
268  }
269
270 #ifdef USE_ITHREADS
271  MUTEX_UNLOCK(&a_op_map_mutex);
272 #endif
273
274  return;
275 }
276
277 STATIC void a_map_update_flags_topdown(const OP *root, UV flags) {
278  a_op_info *oi;
279  const OP *o = root;
280
281 #ifdef USE_ITHREADS
282  MUTEX_LOCK(&a_op_map_mutex);
283 #endif
284
285  flags &= ~A_HINT_ROOT;
286
287  do {
288   if ((oi = ptable_fetch(a_op_map, o)))
289    oi->flags = (oi->flags & A_HINT_ROOT) | flags;
290   if (!(o->op_flags & OPf_KIDS))
291    break;
292   o = a_map_descend(o);
293  } while (o);
294
295 #ifdef USE_ITHREADS
296  MUTEX_UNLOCK(&a_op_map_mutex);
297 #endif
298
299  return;
300 }
301
302 #define a_map_cancel(R) a_map_update_flags_topdown((R), 0)
303
304 STATIC void a_map_update_flags_bottomup(const OP *o, UV flags, UV rflags) {
305  a_op_info *oi;
306
307 #ifdef USE_ITHREADS
308  MUTEX_LOCK(&a_op_map_mutex);
309 #endif
310
311  flags  &= ~A_HINT_ROOT;
312  rflags |=  A_HINT_ROOT;
313
314  oi = ptable_fetch(a_op_map, o);
315  while (!(oi->flags & A_HINT_ROOT)) {
316   oi->flags = flags;
317   oi        = oi->next;
318  }
319  oi->flags = rflags;
320
321 #ifdef USE_ITHREADS
322  MUTEX_UNLOCK(&a_op_map_mutex);
323 #endif
324
325  return;
326 }
327
328 /* ... Decide whether this expression should be autovivified or not ........ */
329
330 STATIC UV a_map_resolve(const OP *o, a_op_info *oi) {
331  UV flags = 0, rflags;
332  const OP *root;
333  a_op_info *roi = oi;
334
335  while (!(roi->flags & A_HINT_ROOT))
336   roi = roi->next;
337  if (!roi)
338   goto cancel;
339
340  rflags = roi->flags & ~A_HINT_ROOT;
341  if (!rflags)
342   goto cancel;
343
344  root = roi->next;
345  if (root->op_flags & OPf_MOD) {
346   if (rflags & A_HINT_STORE)
347    flags = (A_HINT_STORE|A_HINT_DEREF);
348  } else if (rflags & A_HINT_FETCH)
349    flags = (A_HINT_FETCH|A_HINT_DEREF);
350
351  if (!flags) {
352 cancel:
353   a_map_update_flags_bottomup(o, 0, 0);
354   return 0;
355  }
356
357  flags |= (rflags & A_HINT_NOTIFY);
358  a_map_update_flags_bottomup(o, flags, 0);
359
360  return oi->flags & A_HINT_ROOT ? 0 : flags;
361 }
362
363 /* ... Lightweight pp_defined() ............................................ */
364
365 STATIC bool a_defined(pTHX_ SV *sv) {
366 #define a_defined(S) a_defined(aTHX_ (S))
367  bool defined = FALSE;
368
369  switch (SvTYPE(sv)) {
370   case SVt_PVAV:
371    if (AvMAX(sv) >= 0 || SvGMAGICAL(sv)
372                       || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
373     defined = TRUE;
374    break;
375   case SVt_PVHV:
376    if (HvARRAY(sv) || SvGMAGICAL(sv)
377                    || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
378     defined = TRUE;
379    break;
380   default:
381    SvGETMAGIC(sv);
382    if (SvOK(sv))
383     defined = TRUE;
384  }
385
386  return defined;
387 }
388
389 /* --- PP functions -------------------------------------------------------- */
390
391 /* Be aware that we restore PL_op->op_ppaddr from the pointer table old_pp
392  * value, another extension might have saved our pp replacement as the ppaddr
393  * for this op, so this doesn't ensure that our function will never be called
394  * again. That's why we don't remove the op info from our map, so that it can
395  * still run correctly if required. */
396
397 /* ... pp_rv2av ............................................................ */
398
399 STATIC OP *a_pp_rv2av(pTHX) {
400  a_op_info oi;
401  UV flags;
402  dSP;
403
404  a_map_fetch(PL_op, &oi);
405  flags = oi.flags;
406
407  if (flags & A_HINT_DEREF) {
408   if (!a_defined(TOPs)) {
409    /* We always need to push an empty array to fool the pp_aelem() that comes
410     * later. */
411    SV *av;
412    POPs;
413    av = sv_2mortal((SV *) newAV());
414    PUSHs(av);
415    RETURN;
416   }
417  } else {
418   PL_op->op_ppaddr = oi.old_pp;
419  }
420
421  return CALL_FPTR(oi.old_pp)(aTHX);
422 }
423
424 /* ... pp_rv2hv ............................................................ */
425
426 STATIC OP *a_pp_rv2hv_simple(pTHX) {
427  a_op_info oi;
428  UV flags;
429  dSP;
430
431  a_map_fetch(PL_op, &oi);
432  flags = oi.flags;
433
434  if (flags & A_HINT_DEREF) {
435   if (!a_defined(TOPs))
436    RETURN;
437  } else {
438   PL_op->op_ppaddr = oi.old_pp;
439  }
440
441  return CALL_FPTR(oi.old_pp)(aTHX);
442 }
443
444 STATIC OP *a_pp_rv2hv(pTHX) {
445  a_op_info oi;
446  UV flags;
447  dSP;
448
449  a_map_fetch(PL_op, &oi);
450  flags = oi.flags;
451
452  if (flags & A_HINT_DEREF) {
453   if (!a_defined(TOPs)) {
454    SV *hv;
455    POPs;
456    hv = sv_2mortal((SV *) newHV());
457    PUSHs(hv);
458    RETURN;
459   }
460  } else {
461   PL_op->op_ppaddr = oi.old_pp;
462  }
463
464  return CALL_FPTR(oi.old_pp)(aTHX);
465 }
466
467 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
468
469 STATIC OP *a_pp_deref(pTHX) {
470  a_op_info oi;
471  UV flags;
472  dSP;
473
474  a_map_fetch(PL_op, &oi);
475  flags = oi.flags;
476
477  if (flags & A_HINT_DEREF) {
478   OP *o;
479   U8 old_private;
480
481 deref:
482   old_private       = PL_op->op_private;
483   PL_op->op_private = ((old_private & ~OPpDEREF) | OPpLVAL_DEFER);
484   o = CALL_FPTR(oi.old_pp)(aTHX);
485   PL_op->op_private = old_private;
486
487   if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) {
488    SPAGAIN;
489    if (!a_defined(TOPs)) {
490     if (flags & A_HINT_STRICT)
491      croak("Reference vivification forbidden");
492     else if (flags & A_HINT_WARN)
493       warn("Reference was vivified");
494     else /* A_HINT_STORE */
495      croak("Can't vivify reference");
496    }
497   }
498
499   return o;
500  } else if ((flags & ~A_HINT_ROOT)
501                     && (PL_op->op_private & OPpDEREF || flags & A_HINT_ROOT)) {
502   /* Decide if the expression must autovivify or not.
503    * This branch should be called only once by expression. */
504   flags = a_map_resolve(PL_op, &oi);
505
506   /* We need the updated flags value in the deref branch. */
507   if (flags & A_HINT_DEREF)
508    goto deref;
509  }
510
511  /* This op doesn't need to skip autovivification, so restore the original
512   * state. */
513  PL_op->op_ppaddr = oi.old_pp;
514
515  return CALL_FPTR(oi.old_pp)(aTHX);
516 }
517
518 /* ... pp_root (exists,delete,keys,values) ................................. */
519
520 STATIC OP *a_pp_root_unop(pTHX) {
521  a_op_info oi;
522  dSP;
523
524  if (!a_defined(TOPs)) {
525   POPs;
526   /* Can only be reached by keys or values */
527   if (GIMME_V == G_SCALAR) {
528    dTARGET;
529    PUSHi(0);
530   }
531   RETURN;
532  }
533
534  a_map_fetch(PL_op, &oi);
535
536  return CALL_FPTR(oi.old_pp)(aTHX);
537 }
538
539 STATIC OP *a_pp_root_binop(pTHX) {
540  a_op_info oi;
541  dSP;
542
543  if (!a_defined(TOPm1s)) {
544   POPs;
545   POPs;
546   if (PL_op->op_type == OP_EXISTS)
547    RETPUSHNO;
548   else
549    RETPUSHUNDEF;
550  }
551
552  a_map_fetch(PL_op, &oi);
553
554  return CALL_FPTR(oi.old_pp)(aTHX);
555 }
556
557 /* --- Check functions ----------------------------------------------------- */
558
559 STATIC void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) {
560 #define a_recheck_rv2xv(O, T, PP) a_recheck_rv2xv(aTHX_ (O), (T), (PP))
561  a_op_info oi;
562
563  if (o->op_type == type && o->op_ppaddr != new_pp
564                         && cUNOPo->op_first->op_type != OP_GV
565                         && a_map_fetch(o, &oi)) {
566   a_map_store(o, o->op_ppaddr, oi.next, oi.flags);
567   o->op_ppaddr = new_pp;
568  }
569
570  return;
571 }
572
573 /* ... ck_pad{any,sv} ...................................................... */
574
575 /* Sadly, the PADSV OPs we are interested in don't trigger the padsv check
576  * function, but are instead manually mutated from a PADANY. This is why we set
577  * PL_ppaddr[OP_PADSV] in the padany check function so that PADSV OPs will have
578  * their op_ppaddr set to our pp_padsv. PL_ppaddr[OP_PADSV] is then reset at the
579  * beginning of every ck_pad{any,sv}. Some unwanted OPs can still call our
580  * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV]
581  * globally. */
582
583 STATIC OP *(*a_pp_padsv_saved)(pTHX) = 0;
584
585 STATIC void a_pp_padsv_save(void) {
586  if (a_pp_padsv_saved)
587   return;
588
589  a_pp_padsv_saved    = PL_ppaddr[OP_PADSV];
590  PL_ppaddr[OP_PADSV] = a_pp_deref;
591 }
592
593 STATIC void a_pp_padsv_restore(OP *o) {
594  if (!a_pp_padsv_saved)
595   return;
596
597  if (o->op_ppaddr == a_pp_deref)
598   o->op_ppaddr = a_pp_padsv_saved;
599
600  PL_ppaddr[OP_PADSV] = a_pp_padsv_saved;
601  a_pp_padsv_saved    = 0;
602 }
603
604 STATIC OP *(*a_old_ck_padany)(pTHX_ OP *) = 0;
605
606 STATIC OP *a_ck_padany(pTHX_ OP *o) {
607  UV hint;
608
609  a_pp_padsv_restore(o);
610
611  o = CALL_FPTR(a_old_ck_padany)(aTHX_ o);
612
613  hint = a_hint();
614  if (hint & A_HINT_DO) {
615   a_pp_padsv_save();
616   a_map_store_root(o, a_pp_padsv_saved, hint);
617  } else
618   a_map_delete(o);
619
620  return o;
621 }
622
623 STATIC OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0;
624
625 STATIC OP *a_ck_padsv(pTHX_ OP *o) {
626  UV hint;
627
628  a_pp_padsv_restore(o);
629
630  o = CALL_FPTR(a_old_ck_padsv)(aTHX_ o);
631
632  hint = a_hint();
633  if (hint & A_HINT_DO) {
634   a_map_store_root(o, o->op_ppaddr, hint);
635   o->op_ppaddr = a_pp_deref;
636  } else
637   a_map_delete(o);
638
639  return o;
640 }
641
642 /* ... ck_deref (aelem,helem,rv2sv) ........................................ */
643
644 /* Those ops appear both at the root and inside an expression but there's no
645  * way to distinguish both situations. Worse, we can't even know if we are in a
646  * modifying context, so the expression can't be resolved yet. It will be at the
647  * first invocation of a_pp_deref() for this expression. */
648
649 STATIC OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0;
650 STATIC OP *(*a_old_ck_helem)(pTHX_ OP *) = 0;
651 STATIC OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0;
652
653 STATIC OP *a_ck_deref(pTHX_ OP *o) {
654  OP * (*old_ck)(pTHX_ OP *o) = 0;
655  UV hint = a_hint();
656
657  switch (o->op_type) {
658   case OP_AELEM:
659    old_ck = a_old_ck_aelem;
660    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
661     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2AV, a_pp_rv2av);
662    break;
663   case OP_HELEM:
664    old_ck = a_old_ck_helem;
665    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
666     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv_simple);
667    break;
668   case OP_RV2SV:
669    old_ck = a_old_ck_rv2sv;
670    break;
671  }
672  o = CALL_FPTR(old_ck)(aTHX_ o);
673
674  if (hint & A_HINT_DO) {
675   a_map_store_root(o, o->op_ppaddr, hint);
676   o->op_ppaddr = a_pp_deref;
677  } else
678   a_map_delete(o);
679
680  return o;
681 }
682
683 /* ... ck_rv2xv (rv2av,rv2hv) .............................................. */
684
685 /* Those ops also appear both inisde and at the root, hence the caveats for
686  * a_ck_deref() still apply here. Since a padsv/rv2sv must appear before a
687  * rv2[ah]v, resolution is handled by the first call to a_pp_deref() in the
688  * expression. */
689
690 STATIC OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0;
691 STATIC OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0;
692
693 STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
694  OP * (*old_ck)(pTHX_ OP *o) = 0;
695  OP * (*new_pp)(pTHX)        = 0;
696  UV hint;
697
698  switch (o->op_type) {
699   case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
700   case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv_simple; break;
701  }
702  o = CALL_FPTR(old_ck)(aTHX_ o);
703
704  if (cUNOPo->op_first->op_type == OP_GV)
705   return o;
706
707  hint = a_hint();
708  if (hint & A_HINT_DO && !(hint & A_HINT_STRICT)) {
709   a_map_store_root(o, o->op_ppaddr, hint);
710   o->op_ppaddr = new_pp;
711  } else
712   a_map_delete(o);
713
714  return o;
715 }
716
717 /* ... ck_xslice (aslice,hslice) ........................................... */
718
719 /* I think those are only found at the root, but there's nothing that really
720  * prevent them to be inside the expression too. We only need to update the
721  * root so that the rest of the expression will see the right context when
722  * resolving. That's why we don't replace the ppaddr. */
723
724 STATIC OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0;
725 STATIC OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0;
726
727 STATIC OP *a_ck_xslice(pTHX_ OP *o) {
728  OP * (*old_ck)(pTHX_ OP *o) = 0;
729  UV hint = a_hint();
730
731  switch (o->op_type) {
732   case OP_ASLICE:
733    old_ck = a_old_ck_aslice;
734    break;
735   case OP_HSLICE:
736    old_ck = a_old_ck_hslice;
737    if (hint & A_HINT_DO)
738     a_recheck_rv2xv(cUNOPo->op_first->op_sibling, OP_RV2HV, a_pp_rv2hv);
739    break;
740  }
741  o = CALL_FPTR(old_ck)(aTHX_ o);
742
743  if (hint & A_HINT_DO) {
744   a_map_store_root(o, 0, hint);
745  } else
746   a_map_delete(o);
747
748  return o;
749 }
750
751 /* ... ck_root (exists,delete,keys,values) ................................. */
752
753 /* Those ops are only found at the root of a dereferencing expression. We can
754  * then resolve at compile time if vivification must take place or not. */
755
756 STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
757 STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
758 STATIC OP *(*a_old_ck_keys)  (pTHX_ OP *) = 0;
759 STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
760
761 STATIC OP *a_ck_root(pTHX_ OP *o) {
762  OP * (*old_ck)(pTHX_ OP *o) = 0;
763  OP * (*new_pp)(pTHX)        = 0;
764  bool enabled = FALSE;
765  UV hint = a_hint();
766
767  switch (o->op_type) {
768   case OP_EXISTS:
769    old_ck  = a_old_ck_exists;
770    new_pp  = a_pp_root_binop;
771    enabled = hint & A_HINT_EXISTS;
772    break;
773   case OP_DELETE:
774    old_ck  = a_old_ck_delete;
775    new_pp  = a_pp_root_binop;
776    enabled = hint & A_HINT_DELETE;
777    break;
778   case OP_KEYS:
779    old_ck  = a_old_ck_keys;
780    new_pp  = a_pp_root_unop;
781    enabled = hint & A_HINT_FETCH;
782    break;
783   case OP_VALUES:
784    old_ck  = a_old_ck_values;
785    new_pp  = a_pp_root_unop;
786    enabled = hint & A_HINT_FETCH;
787    break;
788  }
789  o = CALL_FPTR(old_ck)(aTHX_ o);
790
791  if (hint & A_HINT_DO) {
792   if (enabled) {
793    a_map_update_flags_topdown(o, hint | A_HINT_DEREF);
794    a_map_store_root(o, o->op_ppaddr, hint);
795    o->op_ppaddr = new_pp;
796   } else {
797    a_map_cancel(o);
798   }
799  } else
800   a_map_delete(o);
801
802  return o;
803 }
804
805 STATIC U32 a_initialized = 0;
806
807 /* --- XS ------------------------------------------------------------------ */
808
809 MODULE = autovivification      PACKAGE = autovivification
810
811 PROTOTYPES: ENABLE
812
813 BOOT: 
814 {                                    
815  if (!a_initialized++) {
816   HV *stash;
817
818   a_op_map = ptable_new();
819 #ifdef USE_ITHREADS
820   MUTEX_INIT(&a_op_map_mutex);
821 #endif
822
823   PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__);
824
825   a_old_ck_padany     = PL_check[OP_PADANY];
826   PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany);
827   a_old_ck_padsv      = PL_check[OP_PADSV];
828   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(a_ck_padsv);
829
830   a_old_ck_aelem      = PL_check[OP_AELEM];
831   PL_check[OP_AELEM]  = MEMBER_TO_FPTR(a_ck_deref);
832   a_old_ck_helem      = PL_check[OP_HELEM];
833   PL_check[OP_HELEM]  = MEMBER_TO_FPTR(a_ck_deref);
834   a_old_ck_rv2sv      = PL_check[OP_RV2SV];
835   PL_check[OP_RV2SV]  = MEMBER_TO_FPTR(a_ck_deref);
836
837   a_old_ck_rv2av      = PL_check[OP_RV2AV];
838   PL_check[OP_RV2AV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
839   a_old_ck_rv2hv      = PL_check[OP_RV2HV];
840   PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
841
842   a_old_ck_aslice     = PL_check[OP_ASLICE];
843   PL_check[OP_ASLICE] = MEMBER_TO_FPTR(a_ck_xslice);
844   a_old_ck_hslice     = PL_check[OP_HSLICE];
845   PL_check[OP_HSLICE] = MEMBER_TO_FPTR(a_ck_xslice);
846
847   a_old_ck_exists     = PL_check[OP_EXISTS];
848   PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
849   a_old_ck_delete     = PL_check[OP_DELETE];
850   PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root);
851   a_old_ck_keys       = PL_check[OP_KEYS];
852   PL_check[OP_KEYS]   = MEMBER_TO_FPTR(a_ck_root);
853   a_old_ck_values     = PL_check[OP_VALUES];
854   PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root);
855
856   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
857   newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));
858   newCONSTSUB(stash, "A_HINT_WARN",   newSVuv(A_HINT_WARN));
859   newCONSTSUB(stash, "A_HINT_FETCH",  newSVuv(A_HINT_FETCH));
860   newCONSTSUB(stash, "A_HINT_STORE",  newSVuv(A_HINT_STORE));
861   newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS));
862   newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE));
863   newCONSTSUB(stash, "A_HINT_MASK",   newSVuv(A_HINT_MASK));
864  }
865 }
866
867 SV *
868 _tag(SV *hint)
869 PROTOTYPE: $
870 CODE:
871  RETVAL = a_tag(SvOK(hint) ? SvUV(hint) : 0);
872 OUTPUT:
873  RETVAL
874
875 SV *
876 _detag(SV *tag)
877 PROTOTYPE: $
878 CODE:
879  if (!SvOK(tag))
880   XSRETURN_UNDEF;
881  RETVAL = newSVuv(a_detag(tag));
882 OUTPUT:
883  RETVAL