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