]> git.vpit.fr Git - perl/modules/autovivification.git/blob - autovivification.xs
Handle non-numeric hints
[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    defined = SvOK(sv);
382  }
383
384  return defined;
385 }
386
387 /* --- PP functions -------------------------------------------------------- */
388
389 /* Be aware that we restore PL_op->op_ppaddr from the pointer table old_pp
390  * value, another extension might have saved our pp replacement as the ppaddr
391  * for this op, so this doesn't ensure that our function will never be called
392  * again. That's why we don't remove the op info from our map, so that it can
393  * still run correctly if required. */
394
395 /* ... pp_rv2av ............................................................ */
396
397 STATIC OP *a_pp_rv2av(pTHX) {
398  a_op_info oi;
399  UV flags;
400  dSP;
401
402  a_map_fetch(PL_op, &oi);
403  flags = oi.flags;
404
405  if (flags & A_HINT_DEREF) {
406   if (!SvOK(TOPs)) {
407    /* We always need to push an empty array to fool the pp_aelem() that comes
408     * later. */
409    SV *av;
410    POPs;
411    av = sv_2mortal((SV *) newAV());
412    PUSHs(av);
413    RETURN;
414   }
415  } else {
416   PL_op->op_ppaddr = oi.old_pp;
417  }
418
419  return CALL_FPTR(oi.old_pp)(aTHX);
420 }
421
422 /* ... pp_rv2hv ............................................................ */
423
424 STATIC OP *a_pp_rv2hv_simple(pTHX) {
425  a_op_info oi;
426  UV flags;
427  dSP;
428
429  a_map_fetch(PL_op, &oi);
430  flags = oi.flags;
431
432  if (flags & A_HINT_DEREF) {
433   if (!SvOK(TOPs))
434    RETURN;
435  } else {
436   PL_op->op_ppaddr = oi.old_pp;
437  }
438
439  return CALL_FPTR(oi.old_pp)(aTHX);
440 }
441
442 STATIC OP *a_pp_rv2hv(pTHX) {
443  a_op_info oi;
444  UV flags;
445  dSP;
446
447  a_map_fetch(PL_op, &oi);
448  flags = oi.flags;
449
450  if (flags & A_HINT_DEREF) {
451   if (!SvOK(TOPs)) {
452    SV *hv;
453    POPs;
454    hv = sv_2mortal((SV *) newHV());
455    PUSHs(hv);
456    RETURN;
457   }
458  } else {
459   PL_op->op_ppaddr = oi.old_pp;
460  }
461
462  return CALL_FPTR(oi.old_pp)(aTHX);
463 }
464
465 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
466
467 STATIC OP *a_pp_deref(pTHX) {
468  a_op_info oi;
469  UV flags;
470  dSP;
471
472  a_map_fetch(PL_op, &oi);
473  flags = oi.flags;
474
475  if (flags & A_HINT_DEREF) {
476   OP *o;
477   U8 old_private;
478
479 deref:
480   old_private       = PL_op->op_private;
481   PL_op->op_private = ((old_private & ~OPpDEREF) | OPpLVAL_DEFER);
482   o = CALL_FPTR(oi.old_pp)(aTHX);
483   PL_op->op_private = old_private;
484
485   if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) {
486    SPAGAIN;
487    if (!SvOK(TOPs)) {
488     if (flags & A_HINT_STRICT)
489      croak("Reference vivification forbidden");
490     else if (flags & A_HINT_WARN)
491       warn("Reference was vivified");
492     else /* A_HINT_STORE */
493      croak("Can't vivify reference");
494    }
495   }
496
497   return o;
498  } else if ((flags & ~A_HINT_ROOT)
499                     && (PL_op->op_private & OPpDEREF || flags & A_HINT_ROOT)) {
500   /* Decide if the expression must autovivify or not.
501    * This branch should be called only once by expression. */
502   flags = a_map_resolve(PL_op, &oi);
503
504   /* We need the updated flags value in the deref branch. */
505   if (flags & A_HINT_DEREF)
506    goto deref;
507  }
508
509  /* This op doesn't need to skip autovivification, so restore the original
510   * state. */
511  PL_op->op_ppaddr = oi.old_pp;
512
513  return CALL_FPTR(oi.old_pp)(aTHX);
514 }
515
516 /* ... pp_root (exists,delete,keys,values) ................................. */
517
518 STATIC OP *a_pp_root_unop(pTHX) {
519  a_op_info oi;
520  dSP;
521
522  if (!a_defined(TOPs)) {
523   POPs;
524   /* Can only be reached by keys or values */
525   if (GIMME_V == G_SCALAR) {
526    dTARGET;
527    PUSHi(0);
528   }
529   RETURN;
530  }
531
532  a_map_fetch(PL_op, &oi);
533
534  return CALL_FPTR(oi.old_pp)(aTHX);
535 }
536
537 STATIC OP *a_pp_root_binop(pTHX) {
538  a_op_info oi;
539  dSP;
540
541  if (!a_defined(TOPm1s)) {
542   POPs;
543   POPs;
544   if (PL_op->op_type == OP_EXISTS)
545    RETPUSHNO;
546   else
547    RETPUSHUNDEF;
548  }
549
550  a_map_fetch(PL_op, &oi);
551
552  return CALL_FPTR(oi.old_pp)(aTHX);
553 }
554
555 /* --- Check functions ----------------------------------------------------- */
556
557 STATIC void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) {
558 #define a_recheck_rv2xv(O, T, PP) a_recheck_rv2xv(aTHX_ (O), (T), (PP))
559  a_op_info oi;
560
561  if (o->op_type == type && o->op_ppaddr != new_pp
562                         && cUNOPo->op_first->op_type != OP_GV
563                         && a_map_fetch(o, &oi)) {
564   a_map_store(o, o->op_ppaddr, oi.next, oi.flags);
565   o->op_ppaddr = new_pp;
566  }
567
568  return;
569 }
570
571 /* ... ck_pad{any,sv} ...................................................... */
572
573 /* Sadly, the PADSV OPs we are interested in don't trigger the padsv check
574  * function, but are instead manually mutated from a PADANY. This is why we set
575  * PL_ppaddr[OP_PADSV] in the padany check function so that PADSV OPs will have
576  * their op_ppaddr set to our pp_padsv. PL_ppaddr[OP_PADSV] is then reset at the
577  * beginning of every ck_pad{any,sv}. Some unwanted OPs can still call our
578  * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV]
579  * globally. */
580
581 STATIC OP *(*a_pp_padsv_saved)(pTHX) = 0;
582
583 STATIC void a_pp_padsv_save(void) {
584  if (a_pp_padsv_saved)
585   return;
586
587  a_pp_padsv_saved    = PL_ppaddr[OP_PADSV];
588  PL_ppaddr[OP_PADSV] = a_pp_deref;
589 }
590
591 STATIC void a_pp_padsv_restore(OP *o) {
592  if (!a_pp_padsv_saved)
593   return;
594
595  if (o->op_ppaddr == a_pp_deref)
596   o->op_ppaddr = a_pp_padsv_saved;
597
598  PL_ppaddr[OP_PADSV] = a_pp_padsv_saved;
599  a_pp_padsv_saved    = 0;
600 }
601
602 STATIC OP *(*a_old_ck_padany)(pTHX_ OP *) = 0;
603
604 STATIC OP *a_ck_padany(pTHX_ OP *o) {
605  UV hint;
606
607  a_pp_padsv_restore(o);
608
609  o = CALL_FPTR(a_old_ck_padany)(aTHX_ o);
610
611  hint = a_hint();
612  if (hint & A_HINT_DO) {
613   a_pp_padsv_save();
614   a_map_store_root(o, a_pp_padsv_saved, hint);
615  } else
616   a_map_delete(o);
617
618  return o;
619 }
620
621 STATIC OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0;
622
623 STATIC OP *a_ck_padsv(pTHX_ OP *o) {
624  UV hint;
625
626  a_pp_padsv_restore(o);
627
628  o = CALL_FPTR(a_old_ck_padsv)(aTHX_ o);
629
630  hint = a_hint();
631  if (hint & A_HINT_DO) {
632   a_map_store_root(o, o->op_ppaddr, hint);
633   o->op_ppaddr = a_pp_deref;
634  } else
635   a_map_delete(o);
636
637  return o;
638 }
639
640 /* ... ck_deref (aelem,helem,rv2sv) ........................................ */
641
642 /* Those ops appear both at the root and inside an expression but there's no
643  * way to distinguish both situations. Worse, we can't even know if we are in a
644  * modifying context, so the expression can't be resolved yet. It will be at the
645  * first invocation of a_pp_deref() for this expression. */
646
647 STATIC OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0;
648 STATIC OP *(*a_old_ck_helem)(pTHX_ OP *) = 0;
649 STATIC OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0;
650
651 STATIC OP *a_ck_deref(pTHX_ OP *o) {
652  OP * (*old_ck)(pTHX_ OP *o) = 0;
653  UV hint = a_hint();
654
655  switch (o->op_type) {
656   case OP_AELEM:
657    old_ck = a_old_ck_aelem;
658    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
659     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2AV, a_pp_rv2av);
660    break;
661   case OP_HELEM:
662    old_ck = a_old_ck_helem;
663    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
664     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv_simple);
665    break;
666   case OP_RV2SV:
667    old_ck = a_old_ck_rv2sv;
668    break;
669  }
670  o = CALL_FPTR(old_ck)(aTHX_ o);
671
672  if (hint & A_HINT_DO) {
673   a_map_store_root(o, o->op_ppaddr, hint);
674   o->op_ppaddr = a_pp_deref;
675  } else
676   a_map_delete(o);
677
678  return o;
679 }
680
681 /* ... ck_rv2xv (rv2av,rv2hv) .............................................. */
682
683 /* Those ops also appear both inisde and at the root, hence the caveats for
684  * a_ck_deref() still apply here. Since a padsv/rv2sv must appear before a
685  * rv2[ah]v, resolution is handled by the first call to a_pp_deref() in the
686  * expression. */
687
688 STATIC OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0;
689 STATIC OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0;
690
691 STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
692  OP * (*old_ck)(pTHX_ OP *o) = 0;
693  OP * (*new_pp)(pTHX)        = 0;
694  UV hint;
695
696  switch (o->op_type) {
697   case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
698   case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv_simple; break;
699  }
700  o = CALL_FPTR(old_ck)(aTHX_ o);
701
702  if (cUNOPo->op_first->op_type == OP_GV)
703   return o;
704
705  hint = a_hint();
706  if (hint & A_HINT_DO && !(hint & A_HINT_STRICT)) {
707   a_map_store_root(o, o->op_ppaddr, hint);
708   o->op_ppaddr = new_pp;
709  } else
710   a_map_delete(o);
711
712  return o;
713 }
714
715 /* ... ck_xslice (aslice,hslice) ........................................... */
716
717 /* I think those are only found at the root, but there's nothing that really
718  * prevent them to be inside the expression too. We only need to update the
719  * root so that the rest of the expression will see the right context when
720  * resolving. That's why we don't replace the ppaddr. */
721
722 STATIC OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0;
723 STATIC OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0;
724
725 STATIC OP *a_ck_xslice(pTHX_ OP *o) {
726  OP * (*old_ck)(pTHX_ OP *o) = 0;
727  UV hint = a_hint();
728
729  switch (o->op_type) {
730   case OP_ASLICE:
731    old_ck = a_old_ck_aslice;
732    break;
733   case OP_HSLICE:
734    old_ck = a_old_ck_hslice;
735    if (hint & A_HINT_DO)
736     a_recheck_rv2xv(cUNOPo->op_first->op_sibling, OP_RV2HV, a_pp_rv2hv);
737    break;
738  }
739  o = CALL_FPTR(old_ck)(aTHX_ o);
740
741  if (hint & A_HINT_DO) {
742   a_map_store_root(o, 0, hint);
743  } else
744   a_map_delete(o);
745
746  return o;
747 }
748
749 /* ... ck_root (exists,delete,keys,values) ................................. */
750
751 /* Those ops are only found at the root of a dereferencing expression. We can
752  * then resolve at compile time if vivification must take place or not. */
753
754 STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
755 STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
756 STATIC OP *(*a_old_ck_keys)  (pTHX_ OP *) = 0;
757 STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
758
759 STATIC OP *a_ck_root(pTHX_ OP *o) {
760  OP * (*old_ck)(pTHX_ OP *o) = 0;
761  OP * (*new_pp)(pTHX)        = 0;
762  bool enabled = FALSE;
763  UV hint = a_hint();
764
765  switch (o->op_type) {
766   case OP_EXISTS:
767    old_ck  = a_old_ck_exists;
768    new_pp  = a_pp_root_binop;
769    enabled = hint & A_HINT_EXISTS;
770    break;
771   case OP_DELETE:
772    old_ck  = a_old_ck_delete;
773    new_pp  = a_pp_root_binop;
774    enabled = hint & A_HINT_DELETE;
775    break;
776   case OP_KEYS:
777    old_ck  = a_old_ck_keys;
778    new_pp  = a_pp_root_unop;
779    enabled = hint & A_HINT_FETCH;
780    break;
781   case OP_VALUES:
782    old_ck  = a_old_ck_values;
783    new_pp  = a_pp_root_unop;
784    enabled = hint & A_HINT_FETCH;
785    break;
786  }
787  o = CALL_FPTR(old_ck)(aTHX_ o);
788
789  if (hint & A_HINT_DO) {
790   if (enabled) {
791    a_map_update_flags_topdown(o, hint | A_HINT_DEREF);
792    a_map_store_root(o, o->op_ppaddr, hint);
793    o->op_ppaddr = new_pp;
794   } else {
795    a_map_cancel(o);
796   }
797  } else
798   a_map_delete(o);
799
800  return o;
801 }
802
803 STATIC U32 a_initialized = 0;
804
805 /* --- XS ------------------------------------------------------------------ */
806
807 MODULE = autovivification      PACKAGE = autovivification
808
809 PROTOTYPES: ENABLE
810
811 BOOT: 
812 {                                    
813  if (!a_initialized++) {
814   HV *stash;
815
816   a_op_map = ptable_new();
817 #ifdef USE_ITHREADS
818   MUTEX_INIT(&a_op_map_mutex);
819 #endif
820
821   PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__);
822
823   a_old_ck_padany     = PL_check[OP_PADANY];
824   PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany);
825   a_old_ck_padsv      = PL_check[OP_PADSV];
826   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(a_ck_padsv);
827
828   a_old_ck_aelem      = PL_check[OP_AELEM];
829   PL_check[OP_AELEM]  = MEMBER_TO_FPTR(a_ck_deref);
830   a_old_ck_helem      = PL_check[OP_HELEM];
831   PL_check[OP_HELEM]  = MEMBER_TO_FPTR(a_ck_deref);
832   a_old_ck_rv2sv      = PL_check[OP_RV2SV];
833   PL_check[OP_RV2SV]  = MEMBER_TO_FPTR(a_ck_deref);
834
835   a_old_ck_rv2av      = PL_check[OP_RV2AV];
836   PL_check[OP_RV2AV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
837   a_old_ck_rv2hv      = PL_check[OP_RV2HV];
838   PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
839
840   a_old_ck_aslice     = PL_check[OP_ASLICE];
841   PL_check[OP_ASLICE] = MEMBER_TO_FPTR(a_ck_xslice);
842   a_old_ck_hslice     = PL_check[OP_HSLICE];
843   PL_check[OP_HSLICE] = MEMBER_TO_FPTR(a_ck_xslice);
844
845   a_old_ck_exists     = PL_check[OP_EXISTS];
846   PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
847   a_old_ck_delete     = PL_check[OP_DELETE];
848   PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root);
849   a_old_ck_keys       = PL_check[OP_KEYS];
850   PL_check[OP_KEYS]   = MEMBER_TO_FPTR(a_ck_root);
851   a_old_ck_values     = PL_check[OP_VALUES];
852   PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root);
853
854   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
855   newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));
856   newCONSTSUB(stash, "A_HINT_WARN",   newSVuv(A_HINT_WARN));
857   newCONSTSUB(stash, "A_HINT_FETCH",  newSVuv(A_HINT_FETCH));
858   newCONSTSUB(stash, "A_HINT_STORE",  newSVuv(A_HINT_STORE));
859   newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS));
860   newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE));
861   newCONSTSUB(stash, "A_HINT_MASK",   newSVuv(A_HINT_MASK));
862  }
863 }
864
865 SV *
866 _tag(SV *hint)
867 PROTOTYPE: $
868 CODE:
869  RETVAL = a_tag(SvOK(hint) ? SvUV(hint) : 0);
870 OUTPUT:
871  RETVAL
872
873 SV *
874 _detag(SV *tag)
875 PROTOTYPE: $
876 CODE:
877  if (!SvOK(tag))
878   XSRETURN_UNDEF;
879  RETVAL = newSVuv(a_detag(tag));
880 OUTPUT:
881  RETVAL