]> git.vpit.fr Git - perl/modules/autovivification.git/blob - autovivification.xs
a36d0a22d46232090ae0b414f1476059f25a10fc
[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 #ifndef HvNAME_get
15 # define HvNAME_get(H) HvNAME(H)
16 #endif
17
18 #ifndef HvNAMELEN_get
19 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
20 #endif
21
22 #define A_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
23
24 #ifndef A_WORKAROUND_REQUIRE_PROPAGATION
25 # define A_WORKAROUND_REQUIRE_PROPAGATION !A_HAS_PERL(5, 10, 1)
26 #endif
27
28 #ifndef A_HAS_RPEEP
29 # define A_HAS_RPEEP A_HAS_PERL(5, 13, 5)
30 #endif
31
32 /* ... Thread safety and multiplicity ...................................... */
33
34 /* Always safe when the workaround isn't needed */
35 #if !A_WORKAROUND_REQUIRE_PROPAGATION
36 # undef A_FORKSAFE
37 # define A_FORKSAFE 1
38 /* Otherwise, safe unless Makefile.PL says it's Win32 */
39 #elif !defined(A_FORKSAFE)
40 # define A_FORKSAFE 1
41 #endif
42
43 #ifndef A_MULTIPLICITY
44 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
45 #  define A_MULTIPLICITY 1
46 # else
47 #  define A_MULTIPLICITY 0
48 # endif
49 #endif
50
51 #ifndef tTHX
52 # define tTHX PerlInterpreter*
53 #endif
54
55 #if A_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV))
56 # define A_THREADSAFE 1
57 # ifndef MY_CXT_CLONE
58 #  define MY_CXT_CLONE \
59     dMY_CXT_SV;                                                      \
60     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
61     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
62     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
63 # endif
64 #else
65 # define A_THREADSAFE 0
66 # undef  dMY_CXT
67 # define dMY_CXT      dNOOP
68 # undef  MY_CXT
69 # define MY_CXT       a_globaldata
70 # undef  START_MY_CXT
71 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
72 # undef  MY_CXT_INIT
73 # define MY_CXT_INIT  NOOP
74 # undef  MY_CXT_CLONE
75 # define MY_CXT_CLONE NOOP
76 #endif
77
78 #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
79 # define A_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
80 # define A_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
81 #else
82 # define A_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
83 # define A_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
84 #endif
85
86 typedef OP *(*a_ck_t)(pTHX_ OP *);
87
88 #ifdef wrap_op_checker
89
90 # define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
91
92 #else
93
94 STATIC void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) {
95 #define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP))
96  A_CHECK_MUTEX_LOCK;
97  if (!*old_ck_p) {
98   *old_ck_p      = PL_check[type];
99   PL_check[type] = new_ck;
100  }
101  A_CHECK_MUTEX_UNLOCK;
102 }
103
104 #endif
105
106 STATIC void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) {
107 #define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP))
108  A_CHECK_MUTEX_LOCK;
109  if (*old_ck_p) {
110   PL_check[type] = *old_ck_p;
111   *old_ck_p      = 0;
112  }
113  A_CHECK_MUTEX_UNLOCK;
114 }
115
116 /* --- Helpers ------------------------------------------------------------- */
117
118 /* ... Thread-safe hints ................................................... */
119
120 #if A_WORKAROUND_REQUIRE_PROPAGATION
121
122 typedef struct {
123  U32 bits;
124  IV  require_tag;
125 } a_hint_t;
126
127 #define A_HINT_FREE(H) PerlMemShared_free(H)
128
129 #if A_THREADSAFE
130
131 #define PTABLE_NAME        ptable_hints
132 #define PTABLE_VAL_FREE(V) A_HINT_FREE(V)
133
134 #define pPTBL  pTHX
135 #define pPTBL_ pTHX_
136 #define aPTBL  aTHX
137 #define aPTBL_ aTHX_
138
139 #include "ptable.h"
140
141 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
142 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
143
144 #endif /* A_THREADSAFE */
145
146 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
147
148 #define PTABLE_NAME        ptable_seen
149 #define PTABLE_VAL_FREE(V) NOOP
150
151 #include "ptable.h"
152
153 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
154 #define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V))
155 #define ptable_seen_clear(T)       ptable_seen_clear(aPTBLMS_ (T))
156 #define ptable_seen_free(T)        ptable_seen_free(aPTBLMS_ (T))
157
158 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
159
160 typedef struct {
161 #if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
162  ptable *tbl;   /* It really is a ptable_hints */
163  tTHX    owner;
164 #endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
165  ptable *seen;  /* It really is a ptable_seen */
166 } my_cxt_t;
167
168 START_MY_CXT
169
170 #if A_THREADSAFE
171
172 #if A_WORKAROUND_REQUIRE_PROPAGATION
173
174 typedef struct {
175  ptable       *tbl;
176 #if A_HAS_PERL(5, 13, 2)
177  CLONE_PARAMS *params;
178 #else
179  CLONE_PARAMS  params;
180 #endif
181 } a_ptable_clone_ud;
182
183 #if A_HAS_PERL(5, 13, 2)
184 # define a_ptable_clone_ud_init(U, T, O) \
185    (U).tbl    = (T); \
186    (U).params = Perl_clone_params_new((O), aTHX)
187 # define a_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
188 # define a_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), (U)->params))
189 #else
190 # define a_ptable_clone_ud_init(U, T, O) \
191    (U).tbl               = (T);     \
192    (U).params.stashes    = newAV(); \
193    (U).params.flags      = 0;       \
194    (U).params.proto_perl = (O)
195 # define a_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
196 # define a_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), &((U)->params)))
197 #endif
198
199 STATIC void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
200  a_ptable_clone_ud *ud = ud_;
201  a_hint_t *h1 = ent->val;
202  a_hint_t *h2;
203
204  h2              = PerlMemShared_malloc(sizeof *h2);
205  h2->bits        = h1->bits;
206  h2->require_tag = PTR2IV(a_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
207
208  ptable_hints_store(ud->tbl, ent->key, h2);
209 }
210
211 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
212
213 STATIC void a_thread_cleanup(pTHX_ void *ud) {
214  dMY_CXT;
215
216 #if A_WORKAROUND_REQUIRE_PROPAGATION
217  ptable_hints_free(MY_CXT.tbl);
218  MY_CXT.tbl  = NULL;
219 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
220  ptable_seen_free(MY_CXT.seen);
221  MY_CXT.seen = NULL;
222 }
223
224 STATIC int a_endav_free(pTHX_ SV *sv, MAGIC *mg) {
225  SAVEDESTRUCTOR_X(a_thread_cleanup, NULL);
226
227  return 0;
228 }
229
230 STATIC MGVTBL a_endav_vtbl = {
231  0,
232  0,
233  0,
234  0,
235  a_endav_free
236 #if MGf_COPY
237  , 0
238 #endif
239 #if MGf_DUP
240  , 0
241 #endif
242 #if MGf_LOCAL
243  , 0
244 #endif
245 };
246
247 #endif /* A_THREADSAFE */
248
249 #if A_WORKAROUND_REQUIRE_PROPAGATION
250
251 STATIC IV a_require_tag(pTHX) {
252 #define a_require_tag() a_require_tag(aTHX)
253  const CV *cv, *outside;
254
255  cv = PL_compcv;
256
257  if (!cv) {
258   /* If for some reason the pragma is operational at run-time, try to discover
259    * the current cv in use. */
260   const PERL_SI *si;
261
262   for (si = PL_curstackinfo; si; si = si->si_prev) {
263    I32 cxix;
264
265    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
266     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
267
268     switch (CxTYPE(cx)) {
269      case CXt_SUB:
270      case CXt_FORMAT:
271       /* The propagation workaround is only needed up to 5.10.0 and at that
272        * time format and sub contexts were still identical. And even later the
273        * cv members offsets should have been kept the same. */
274       cv = cx->blk_sub.cv;
275       goto get_enclosing_cv;
276      case CXt_EVAL:
277       cv = cx->blk_eval.cv;
278       goto get_enclosing_cv;
279      default:
280       break;
281     }
282    }
283   }
284
285   cv = PL_main_cv;
286  }
287
288 get_enclosing_cv:
289  for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
290   cv = outside;
291
292  return PTR2IV(cv);
293 }
294
295 STATIC SV *a_tag(pTHX_ UV bits) {
296 #define a_tag(B) a_tag(aTHX_ (B))
297  a_hint_t *h;
298 #if A_THREADSAFE
299  dMY_CXT;
300
301  if (!MY_CXT.tbl)
302   return newSViv(0);
303 #endif /* A_THREADSAFE */
304
305  h              = PerlMemShared_malloc(sizeof *h);
306  h->bits        = bits;
307  h->require_tag = a_require_tag();
308
309 #if A_THREADSAFE
310  /* We only need for the key to be an unique tag for looking up the value later
311   * Allocated memory provides convenient unique identifiers, so that's why we
312   * use the hint as the key itself. */
313  ptable_hints_store(MY_CXT.tbl, h, h);
314 #endif /* A_THREADSAFE */
315
316  return newSViv(PTR2IV(h));
317 }
318
319 STATIC UV a_detag(pTHX_ const SV *hint) {
320 #define a_detag(H) a_detag(aTHX_ (H))
321  a_hint_t *h;
322 #if A_THREADSAFE
323  dMY_CXT;
324
325  if (!MY_CXT.tbl)
326   return 0;
327 #endif /* A_THREADSAFE */
328
329  if (!(hint && SvIOK(hint)))
330   return 0;
331
332  h = INT2PTR(a_hint_t *, SvIVX(hint));
333 #if A_THREADSAFE
334  h = ptable_fetch(MY_CXT.tbl, h);
335 #endif /* A_THREADSAFE */
336
337  if (a_require_tag() != h->require_tag)
338   return 0;
339
340  return h->bits;
341 }
342
343 #else /* A_WORKAROUND_REQUIRE_PROPAGATION */
344
345 #define a_tag(B)   newSVuv(B)
346 /* PVs fetched from the hints chain have their SvLEN set to zero, so get the UV
347  * from a copy. */
348 #define a_detag(H) \
349  ((H)              \
350   ? (SvIOK(H)      \
351      ? SvUVX(H)    \
352      : (SvPOK(H)   \
353         ? sv_2uv(SvLEN(H) ? (H) : sv_mortalcopy(H)) \
354         : 0        \
355        )           \
356      )             \
357   : 0)
358
359 #endif /* !A_WORKAROUND_REQUIRE_PROPAGATION */
360
361 /* Used both for hints and op flags */
362 #define A_HINT_STRICT 1
363 #define A_HINT_WARN   2
364 #define A_HINT_FETCH  4
365 #define A_HINT_STORE  8
366 #define A_HINT_EXISTS 16
367 #define A_HINT_DELETE 32
368 #define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN)
369 #define A_HINT_DO     (A_HINT_FETCH|A_HINT_STORE|A_HINT_EXISTS|A_HINT_DELETE)
370 #define A_HINT_MASK   (A_HINT_NOTIFY|A_HINT_DO)
371
372 /* Only used in op flags */
373 #define A_HINT_ROOT   64
374 #define A_HINT_DEREF  128
375
376 STATIC U32 a_hash = 0;
377
378 STATIC UV a_hint(pTHX) {
379 #define a_hint() a_hint(aTHX)
380  SV *hint;
381 #ifdef cop_hints_fetch_pvn
382  hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__, a_hash, 0);
383 #elif A_HAS_PERL(5, 9, 5)
384  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
385                                        NULL,
386                                        __PACKAGE__, __PACKAGE_LEN__,
387                                        0,
388                                        a_hash);
389 #else
390  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
391  if (!val)
392   return 0;
393  hint = *val;
394 #endif
395  return a_detag(hint);
396 }
397
398 /* ... op => info map ...................................................... */
399
400 typedef struct {
401  OP   *(*old_pp)(pTHX);
402  void   *next;
403  UV      flags;
404 } a_op_info;
405
406 #define PTABLE_NAME        ptable_map
407 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
408
409 #include "ptable.h"
410
411 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
412 #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
413 #define ptable_map_delete(T, K)   ptable_map_delete(aPTBLMS_ (T), (K))
414
415 STATIC ptable *a_op_map = NULL;
416
417 #ifdef USE_ITHREADS
418
419 #define dA_MAP_THX a_op_info a_op_map_tmp_oi
420
421 STATIC perl_mutex a_op_map_mutex;
422
423 #define A_LOCK(M)   MUTEX_LOCK(M)
424 #define A_UNLOCK(M) MUTEX_UNLOCK(M)
425
426 STATIC const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) {
427  const a_op_info *val;
428
429  A_LOCK(&a_op_map_mutex);
430
431  val = ptable_fetch(a_op_map, o);
432  if (val) {
433   *oi = *val;
434   val = oi;
435  }
436
437  A_UNLOCK(&a_op_map_mutex);
438
439  return val;
440 }
441
442 #define a_map_fetch(O) a_map_fetch((O), &a_op_map_tmp_oi)
443
444 #else /* USE_ITHREADS */
445
446 #define dA_MAP_THX dNOOP
447
448 #define A_LOCK(M)   NOOP
449 #define A_UNLOCK(M) NOOP
450
451 #define a_map_fetch(O) ptable_fetch(a_op_map, (O))
452
453 #endif /* !USE_ITHREADS */
454
455 STATIC const a_op_info *a_map_store_locked(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
456 #define a_map_store_locked(O, PP, N, F) a_map_store_locked(aPTBLMS_ (O), (PP), (N), (F))
457  a_op_info *oi;
458
459  if (!(oi = ptable_fetch(a_op_map, o))) {
460   oi = PerlMemShared_malloc(sizeof *oi);
461   ptable_map_store(a_op_map, o, oi);
462  }
463
464  oi->old_pp = old_pp;
465  oi->next   = next;
466  oi->flags  = flags;
467
468  return oi;
469 }
470
471 STATIC void a_map_store(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) {
472 #define a_map_store(O, PP, N, F) a_map_store(aPTBLMS_ (O), (PP), (N), (F))
473  A_LOCK(&a_op_map_mutex);
474
475  a_map_store_locked(o, old_pp, next, flags);
476
477  A_UNLOCK(&a_op_map_mutex);
478 }
479
480 STATIC void a_map_delete(pTHX_ const OP *o) {
481 #define a_map_delete(O) a_map_delete(aTHX_ (O))
482  A_LOCK(&a_op_map_mutex);
483
484  ptable_map_delete(a_op_map, o);
485
486  A_UNLOCK(&a_op_map_mutex);
487 }
488
489 STATIC const OP *a_map_descend(const OP *o) {
490  switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
491   case OA_BASEOP:
492   case OA_UNOP:
493   case OA_BINOP:
494   case OA_BASEOP_OR_UNOP:
495    return cUNOPo->op_first;
496   case OA_LIST:
497   case OA_LISTOP:
498    return cLISTOPo->op_last;
499  }
500
501  return NULL;
502 }
503
504 STATIC void a_map_store_root(pPTBLMS_ const OP *root, OP *(*old_pp)(pTHX), UV flags) {
505 #define a_map_store_root(R, PP, F) a_map_store_root(aPTBLMS_ (R), (PP), (F))
506  const a_op_info *roi;
507  a_op_info *oi;
508  const OP *o = root;
509
510  A_LOCK(&a_op_map_mutex);
511
512  roi = a_map_store_locked(o, old_pp, (OP *) root, flags | A_HINT_ROOT);
513
514  while (o->op_flags & OPf_KIDS) {
515   o = a_map_descend(o);
516   if (!o)
517    break;
518   if ((oi = ptable_fetch(a_op_map, o))) {
519    oi->flags &= ~A_HINT_ROOT;
520    oi->next   = (a_op_info *) roi;
521    break;
522   }
523  }
524
525  A_UNLOCK(&a_op_map_mutex);
526
527  return;
528 }
529
530 STATIC void a_map_update_flags_topdown(const OP *root, UV flags) {
531  a_op_info *oi;
532  const OP *o = root;
533
534  A_LOCK(&a_op_map_mutex);
535
536  flags &= ~A_HINT_ROOT;
537
538  do {
539   if ((oi = ptable_fetch(a_op_map, o)))
540    oi->flags = (oi->flags & A_HINT_ROOT) | flags;
541   if (!(o->op_flags & OPf_KIDS))
542    break;
543   o = a_map_descend(o);
544  } while (o);
545
546  A_UNLOCK(&a_op_map_mutex);
547
548  return;
549 }
550
551 #define a_map_cancel(R) a_map_update_flags_topdown((R), 0)
552
553 STATIC void a_map_update_flags_bottomup(const OP *o, UV flags, UV rflags) {
554  a_op_info *oi;
555
556  A_LOCK(&a_op_map_mutex);
557
558  flags  &= ~A_HINT_ROOT;
559  rflags |=  A_HINT_ROOT;
560
561  oi = ptable_fetch(a_op_map, o);
562  while (!(oi->flags & A_HINT_ROOT)) {
563   oi->flags = flags;
564   oi        = oi->next;
565  }
566  oi->flags = rflags;
567
568  A_UNLOCK(&a_op_map_mutex);
569
570  return;
571 }
572
573 /* ... Decide whether this expression should be autovivified or not ........ */
574
575 STATIC UV a_map_resolve(const OP *o, const a_op_info *oi) {
576  UV flags = 0, rflags;
577  const OP *root;
578  const a_op_info *roi = oi;
579
580  while (!(roi->flags & A_HINT_ROOT))
581   roi = roi->next;
582  if (!roi)
583   goto cancel;
584
585  rflags = roi->flags & ~A_HINT_ROOT;
586  if (!rflags)
587   goto cancel;
588
589  root = roi->next;
590  if (root->op_flags & OPf_MOD) {
591   if (rflags & A_HINT_STORE)
592    flags = (A_HINT_STORE|A_HINT_DEREF);
593  } else if (rflags & A_HINT_FETCH)
594    flags = (A_HINT_FETCH|A_HINT_DEREF);
595
596  if (!flags) {
597 cancel:
598   a_map_update_flags_bottomup(o, 0, 0);
599   return 0;
600  }
601
602  flags |= (rflags & A_HINT_NOTIFY);
603  a_map_update_flags_bottomup(o, flags, 0);
604
605  return oi->flags & A_HINT_ROOT ? 0 : flags;
606 }
607
608 /* ... Inspired from pp_defined() .......................................... */
609
610 STATIC int a_undef(pTHX_ SV *sv) {
611 #define a_undef(S) a_undef(aTHX_ (S))
612  switch (SvTYPE(sv)) {
613   case SVt_NULL:
614    return 1;
615   case SVt_PVAV:
616    if (AvMAX(sv) >= 0 || SvGMAGICAL(sv)
617                       || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
618     return 0;
619    break;
620   case SVt_PVHV:
621    if (HvARRAY(sv) || SvGMAGICAL(sv)
622                    || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
623     return 0;
624    break;
625   default:
626    SvGETMAGIC(sv);
627    if (SvOK(sv))
628     return 0;
629  }
630
631  return 1;
632 }
633
634 /* --- PP functions -------------------------------------------------------- */
635
636 /* Be aware that we restore PL_op->op_ppaddr from the pointer table old_pp
637  * value, another extension might have saved our pp replacement as the ppaddr
638  * for this op, so this doesn't ensure that our function will never be called
639  * again. That's why we don't remove the op info from our map, so that it can
640  * still run correctly if required. */
641
642 /* ... pp_rv2av ............................................................ */
643
644 STATIC OP *a_pp_rv2av(pTHX) {
645  dA_MAP_THX;
646  const a_op_info *oi;
647  dSP;
648
649  oi = a_map_fetch(PL_op);
650
651  if (oi->flags & A_HINT_DEREF) {
652   if (a_undef(TOPs)) {
653    /* We always need to push an empty array to fool the pp_aelem() that comes
654     * later. */
655    SV *av;
656    (void) POPs;
657    av = sv_2mortal((SV *) newAV());
658    PUSHs(av);
659    RETURN;
660   }
661  }
662
663  return oi->old_pp(aTHX);
664 }
665
666 /* ... pp_rv2hv ............................................................ */
667
668 STATIC OP *a_pp_rv2hv_simple(pTHX) {
669  dA_MAP_THX;
670  const a_op_info *oi;
671  dSP;
672
673  oi = a_map_fetch(PL_op);
674
675  if (oi->flags & A_HINT_DEREF) {
676   if (a_undef(TOPs))
677    RETURN;
678  }
679
680  return oi->old_pp(aTHX);
681 }
682
683 STATIC OP *a_pp_rv2hv(pTHX) {
684  dA_MAP_THX;
685  const a_op_info *oi;
686  dSP;
687
688  oi = a_map_fetch(PL_op);
689
690  if (oi->flags & A_HINT_DEREF) {
691   if (a_undef(TOPs)) {
692    SV *hv;
693    (void) POPs;
694    hv = sv_2mortal((SV *) newHV());
695    PUSHs(hv);
696    RETURN;
697   }
698  }
699
700  return oi->old_pp(aTHX);
701 }
702
703 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
704
705 STATIC OP *a_pp_deref(pTHX) {
706  dA_MAP_THX;
707  const a_op_info *oi;
708  UV flags;
709  dSP;
710
711  oi = a_map_fetch(PL_op);
712
713  flags = oi->flags;
714  if (flags & A_HINT_DEREF) {
715   OP *o;
716
717   o = oi->old_pp(aTHX);
718
719   if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) {
720    SPAGAIN;
721    if (a_undef(TOPs)) {
722     if (flags & A_HINT_STRICT)
723      croak("Reference vivification forbidden");
724     else if (flags & A_HINT_WARN)
725       warn("Reference was vivified");
726     else /* A_HINT_STORE */
727      croak("Can't vivify reference");
728    }
729   }
730
731   return o;
732  }
733
734  return oi->old_pp(aTHX);
735 }
736
737 /* ... pp_root (exists,delete,keys,values) ................................. */
738
739 STATIC OP *a_pp_root_unop(pTHX) {
740  dSP;
741
742  if (a_undef(TOPs)) {
743   (void) POPs;
744   /* Can only be reached by keys or values */
745   if (GIMME_V == G_SCALAR) {
746    dTARGET;
747    PUSHi(0);
748   }
749   RETURN;
750  }
751
752  {
753   dA_MAP_THX;
754   const a_op_info *oi = a_map_fetch(PL_op);
755   return oi->old_pp(aTHX);
756  }
757 }
758
759 STATIC OP *a_pp_root_binop(pTHX) {
760  dSP;
761
762  if (a_undef(TOPm1s)) {
763   (void) POPs;
764   (void) POPs;
765   if (PL_op->op_type == OP_EXISTS)
766    RETPUSHNO;
767   else
768    RETPUSHUNDEF;
769  }
770
771  {
772   dA_MAP_THX;
773   const a_op_info *oi = a_map_fetch(PL_op);
774   return oi->old_pp(aTHX);
775  }
776 }
777
778 /* --- Check functions ----------------------------------------------------- */
779
780 STATIC void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) {
781 #define a_recheck_rv2xv(O, T, PP) a_recheck_rv2xv(aTHX_ (O), (T), (PP))
782
783  if (o->op_type == type && o->op_ppaddr != new_pp
784                         && cUNOPo->op_first->op_type != OP_GV) {
785   dA_MAP_THX;
786   const a_op_info *oi = a_map_fetch(o);
787   if (oi) {
788    a_map_store(o, o->op_ppaddr, oi->next, oi->flags);
789    o->op_ppaddr = new_pp;
790   }
791  }
792
793  return;
794 }
795
796 /* ... ck_pad{any,sv} ...................................................... */
797
798 /* Sadly, the padsv OPs we are interested in don't trigger the padsv check
799  * function, but are instead manually mutated from a padany. So we store
800  * the op entry in the op map in the padany check function, and we set their
801  * op_ppaddr member in our peephole optimizer replacement below. */
802
803 STATIC OP *(*a_old_ck_padany)(pTHX_ OP *) = 0;
804
805 STATIC OP *a_ck_padany(pTHX_ OP *o) {
806  UV hint;
807
808  o = a_old_ck_padany(aTHX_ o);
809
810  hint = a_hint();
811  if (hint & A_HINT_DO)
812   a_map_store_root(o, o->op_ppaddr, hint);
813  else
814   a_map_delete(o);
815
816  return o;
817 }
818
819 STATIC OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0;
820
821 STATIC OP *a_ck_padsv(pTHX_ OP *o) {
822  UV hint;
823
824  o = a_old_ck_padsv(aTHX_ o);
825
826  hint = a_hint();
827  if (hint & A_HINT_DO) {
828   a_map_store_root(o, o->op_ppaddr, hint);
829   o->op_ppaddr = a_pp_deref;
830  } else
831   a_map_delete(o);
832
833  return o;
834 }
835
836 /* ... ck_deref (aelem,helem,rv2sv) ........................................ */
837
838 /* Those ops appear both at the root and inside an expression but there's no
839  * way to distinguish both situations. Worse, we can't even know if we are in a
840  * modifying context, so the expression can't be resolved yet. It will be at the
841  * first invocation of a_pp_deref() for this expression. */
842
843 STATIC OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0;
844 STATIC OP *(*a_old_ck_helem)(pTHX_ OP *) = 0;
845 STATIC OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0;
846
847 STATIC OP *a_ck_deref(pTHX_ OP *o) {
848  OP * (*old_ck)(pTHX_ OP *o) = 0;
849  UV hint = a_hint();
850
851  switch (o->op_type) {
852   case OP_AELEM:
853    old_ck = a_old_ck_aelem;
854    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
855     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2AV, a_pp_rv2av);
856    break;
857   case OP_HELEM:
858    old_ck = a_old_ck_helem;
859    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
860     a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv_simple);
861    break;
862   case OP_RV2SV:
863    old_ck = a_old_ck_rv2sv;
864    break;
865  }
866  o = old_ck(aTHX_ o);
867
868  if (hint & A_HINT_DO) {
869   a_map_store_root(o, o->op_ppaddr, hint);
870   o->op_ppaddr = a_pp_deref;
871  } else
872   a_map_delete(o);
873
874  return o;
875 }
876
877 /* ... ck_rv2xv (rv2av,rv2hv) .............................................. */
878
879 /* Those ops also appear both inisde and at the root, hence the caveats for
880  * a_ck_deref() still apply here. Since a padsv/rv2sv must appear before a
881  * rv2[ah]v, resolution is handled by the first call to a_pp_deref() in the
882  * expression. */
883
884 STATIC OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0;
885 STATIC OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0;
886
887 STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
888  OP * (*old_ck)(pTHX_ OP *o) = 0;
889  OP * (*new_pp)(pTHX)        = 0;
890  UV hint;
891
892  switch (o->op_type) {
893   case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
894   case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv_simple; break;
895  }
896  o = old_ck(aTHX_ o);
897
898  if (cUNOPo->op_first->op_type == OP_GV)
899   return o;
900
901  hint = a_hint();
902  if (hint & A_HINT_DO && !(hint & A_HINT_STRICT)) {
903   a_map_store_root(o, o->op_ppaddr, hint);
904   o->op_ppaddr = new_pp;
905  } else
906   a_map_delete(o);
907
908  return o;
909 }
910
911 /* ... ck_xslice (aslice,hslice) ........................................... */
912
913 /* I think those are only found at the root, but there's nothing that really
914  * prevent them to be inside the expression too. We only need to update the
915  * root so that the rest of the expression will see the right context when
916  * resolving. That's why we don't replace the ppaddr. */
917
918 STATIC OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0;
919 STATIC OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0;
920
921 STATIC OP *a_ck_xslice(pTHX_ OP *o) {
922  OP * (*old_ck)(pTHX_ OP *o) = 0;
923  UV hint = a_hint();
924
925  switch (o->op_type) {
926   case OP_ASLICE:
927    old_ck = a_old_ck_aslice;
928    break;
929   case OP_HSLICE:
930    old_ck = a_old_ck_hslice;
931    if (hint & A_HINT_DO)
932     a_recheck_rv2xv(cUNOPo->op_first->op_sibling, OP_RV2HV, a_pp_rv2hv);
933    break;
934  }
935  o = old_ck(aTHX_ o);
936
937  if (hint & A_HINT_DO) {
938   a_map_store_root(o, 0, hint);
939  } else
940   a_map_delete(o);
941
942  return o;
943 }
944
945 /* ... ck_root (exists,delete,keys,values) ................................. */
946
947 /* Those ops are only found at the root of a dereferencing expression. We can
948  * then resolve at compile time if vivification must take place or not. */
949
950 STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
951 STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
952 STATIC OP *(*a_old_ck_keys)  (pTHX_ OP *) = 0;
953 STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
954
955 STATIC OP *a_ck_root(pTHX_ OP *o) {
956  OP * (*old_ck)(pTHX_ OP *o) = 0;
957  OP * (*new_pp)(pTHX)        = 0;
958  bool enabled = FALSE;
959  UV hint = a_hint();
960
961  switch (o->op_type) {
962   case OP_EXISTS:
963    old_ck  = a_old_ck_exists;
964    new_pp  = a_pp_root_binop;
965    enabled = hint & A_HINT_EXISTS;
966    break;
967   case OP_DELETE:
968    old_ck  = a_old_ck_delete;
969    new_pp  = a_pp_root_binop;
970    enabled = hint & A_HINT_DELETE;
971    break;
972   case OP_KEYS:
973    old_ck  = a_old_ck_keys;
974    new_pp  = a_pp_root_unop;
975    enabled = hint & A_HINT_FETCH;
976    break;
977   case OP_VALUES:
978    old_ck  = a_old_ck_values;
979    new_pp  = a_pp_root_unop;
980    enabled = hint & A_HINT_FETCH;
981    break;
982  }
983  o = old_ck(aTHX_ o);
984
985  if (hint & A_HINT_DO) {
986   if (enabled) {
987    a_map_update_flags_topdown(o, hint | A_HINT_DEREF);
988    a_map_store_root(o, o->op_ppaddr, hint);
989    o->op_ppaddr = new_pp;
990   } else {
991    a_map_cancel(o);
992   }
993  } else
994   a_map_delete(o);
995
996  return o;
997 }
998
999 /* ... Our peephole optimizer .............................................. */
1000
1001 STATIC peep_t a_old_peep = 0; /* This is actually the rpeep past 5.13.5 */
1002
1003 STATIC void a_peep_rec(pTHX_ OP *o, ptable *seen);
1004
1005 STATIC void a_peep_rec(pTHX_ OP *o, ptable *seen) {
1006 #define a_peep_rec(O) a_peep_rec(aTHX_ (O), seen)
1007  for (; o; o = o->op_next) {
1008   dA_MAP_THX;
1009   const a_op_info *oi = NULL;
1010   UV flags = 0;
1011
1012 #if !A_HAS_RPEEP
1013   if (ptable_fetch(seen, o))
1014    break;
1015   ptable_seen_store(seen, o, o);
1016 #endif
1017
1018   switch (o->op_type) {
1019 #if A_HAS_RPEEP
1020    case OP_NEXTSTATE:
1021    case OP_DBSTATE:
1022    case OP_STUB:
1023     if (ptable_fetch(seen, o))
1024      return;
1025     ptable_seen_store(seen, o, o);
1026     break;
1027 #endif
1028    case OP_PADSV:
1029     if (o->op_ppaddr != a_pp_deref) {
1030      oi = a_map_fetch(o);
1031      if (oi && (oi->flags & A_HINT_DO)) {
1032       a_map_store(o, o->op_ppaddr, oi->next, oi->flags);
1033       o->op_ppaddr = a_pp_deref;
1034      }
1035     }
1036     /* FALLTHROUGH */
1037    case OP_AELEM:
1038    case OP_AELEMFAST:
1039    case OP_HELEM:
1040    case OP_RV2SV:
1041     if (o->op_ppaddr != a_pp_deref)
1042      break;
1043     oi = a_map_fetch(o);
1044     if (!oi)
1045      break;
1046     flags = oi->flags;
1047     if (!(flags & A_HINT_DEREF)
1048         && (flags & A_HINT_DO)
1049         && (o->op_private & OPpDEREF || flags & A_HINT_ROOT)) {
1050      /* Decide if the expression must autovivify or not. */
1051      flags = a_map_resolve(o, oi);
1052     }
1053     if (flags & A_HINT_DEREF)
1054      o->op_private = ((o->op_private & ~OPpDEREF) | OPpLVAL_DEFER);
1055     else
1056      o->op_ppaddr  = oi->old_pp;
1057     break;
1058    case OP_RV2AV:
1059    case OP_RV2HV:
1060     if (   o->op_ppaddr != a_pp_rv2av
1061         && o->op_ppaddr != a_pp_rv2hv
1062         && o->op_ppaddr != a_pp_rv2hv_simple)
1063      break;
1064     oi = a_map_fetch(o);
1065     if (!oi)
1066      break;
1067     if (!(oi->flags & A_HINT_DEREF))
1068      o->op_ppaddr  = oi->old_pp;
1069     break;
1070 #if !A_HAS_RPEEP
1071    case OP_MAPWHILE:
1072    case OP_GREPWHILE:
1073    case OP_AND:
1074    case OP_OR:
1075    case OP_ANDASSIGN:
1076    case OP_ORASSIGN:
1077    case OP_COND_EXPR:
1078    case OP_RANGE:
1079 # if A_HAS_PERL(5, 10, 0)
1080    case OP_ONCE:
1081    case OP_DOR:
1082    case OP_DORASSIGN:
1083 # endif
1084     a_peep_rec(cLOGOPo->op_other);
1085     break;
1086    case OP_ENTERLOOP:
1087    case OP_ENTERITER:
1088     a_peep_rec(cLOOPo->op_redoop);
1089     a_peep_rec(cLOOPo->op_nextop);
1090     a_peep_rec(cLOOPo->op_lastop);
1091     break;
1092 # if A_HAS_PERL(5, 9, 5)
1093    case OP_SUBST:
1094     a_peep_rec(cPMOPo->op_pmstashstartu.op_pmreplstart);
1095     break;
1096 # else
1097    case OP_QR:
1098    case OP_MATCH:
1099    case OP_SUBST:
1100     a_peep_rec(cPMOPo->op_pmreplstart);
1101     break;
1102 # endif
1103 #endif /* !A_HAS_RPEEP */
1104    default:
1105     break;
1106   }
1107  }
1108 }
1109
1110 STATIC void a_peep(pTHX_ OP *o) {
1111  dMY_CXT;
1112  ptable *seen = MY_CXT.seen;
1113
1114  a_old_peep(aTHX_ o);
1115
1116  if (seen) {
1117   ptable_seen_clear(seen);
1118   a_peep_rec(o);
1119   ptable_seen_clear(seen);
1120  }
1121 }
1122
1123 /* --- Interpreter setup/teardown ------------------------------------------ */
1124
1125 STATIC U32 a_initialized = 0;
1126
1127 STATIC void a_teardown(pTHX_ void *root) {
1128
1129  if (!a_initialized)
1130   return;
1131
1132 #if A_MULTIPLICITY
1133  if (aTHX != root)
1134   return;
1135 #endif
1136
1137  {
1138   dMY_CXT;
1139 # if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
1140   ptable_hints_free(MY_CXT.tbl);
1141   MY_CXT.tbl  = NULL;
1142 # endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
1143   ptable_seen_free(MY_CXT.seen);
1144   MY_CXT.seen = NULL;
1145  }
1146
1147  a_ck_restore(OP_PADANY, &a_old_ck_padany);
1148  a_ck_restore(OP_PADSV,  &a_old_ck_padsv);
1149
1150  a_ck_restore(OP_AELEM,  &a_old_ck_aelem);
1151  a_ck_restore(OP_HELEM,  &a_old_ck_helem);
1152  a_ck_restore(OP_RV2SV,  &a_old_ck_rv2sv);
1153
1154  a_ck_restore(OP_RV2AV,  &a_old_ck_rv2av);
1155  a_ck_restore(OP_RV2HV,  &a_old_ck_rv2hv);
1156
1157  a_ck_restore(OP_ASLICE, &a_old_ck_aslice);
1158  a_ck_restore(OP_HSLICE, &a_old_ck_hslice);
1159
1160  a_ck_restore(OP_EXISTS, &a_old_ck_exists);
1161  a_ck_restore(OP_DELETE, &a_old_ck_delete);
1162  a_ck_restore(OP_KEYS,   &a_old_ck_keys);
1163  a_ck_restore(OP_VALUES, &a_old_ck_values);
1164
1165 #if A_HAS_RPEEP
1166  PL_rpeepp  = a_old_peep;
1167 #else
1168  PL_peepp   = a_old_peep;
1169 #endif
1170  a_old_peep = 0;
1171
1172  a_initialized = 0;
1173 }
1174
1175 STATIC void a_setup(pTHX) {
1176 #define a_setup() a_setup(aTHX)
1177  if (a_initialized)
1178   return;
1179
1180  {
1181   MY_CXT_INIT;
1182 # if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
1183   MY_CXT.tbl   = ptable_new();
1184   MY_CXT.owner = aTHX;
1185 # endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
1186   MY_CXT.seen  = ptable_new();
1187  }
1188
1189  a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany);
1190  a_ck_replace(OP_PADSV,  a_ck_padsv,  &a_old_ck_padsv);
1191
1192  a_ck_replace(OP_AELEM,  a_ck_deref,  &a_old_ck_aelem);
1193  a_ck_replace(OP_HELEM,  a_ck_deref,  &a_old_ck_helem);
1194  a_ck_replace(OP_RV2SV,  a_ck_deref,  &a_old_ck_rv2sv);
1195
1196  a_ck_replace(OP_RV2AV,  a_ck_rv2xv,  &a_old_ck_rv2av);
1197  a_ck_replace(OP_RV2HV,  a_ck_rv2xv,  &a_old_ck_rv2hv);
1198
1199  a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice);
1200  a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice);
1201
1202  a_ck_replace(OP_EXISTS, a_ck_root,   &a_old_ck_exists);
1203  a_ck_replace(OP_DELETE, a_ck_root,   &a_old_ck_delete);
1204  a_ck_replace(OP_KEYS,   a_ck_root,   &a_old_ck_keys);
1205  a_ck_replace(OP_VALUES, a_ck_root,   &a_old_ck_values);
1206
1207 #if A_HAS_RPEEP
1208  a_old_peep = PL_rpeepp;
1209  PL_rpeepp  = a_peep;
1210 #else
1211  a_old_peep = PL_peepp;
1212  PL_peepp   = a_peep;
1213 #endif
1214
1215 #if A_MULTIPLICITY
1216  call_atexit(a_teardown, aTHX);
1217 #else
1218  call_atexit(a_teardown, NULL);
1219 #endif
1220
1221  a_initialized = 1;
1222 }
1223
1224 STATIC U32 a_booted = 0;
1225
1226 /* --- XS ------------------------------------------------------------------ */
1227
1228 MODULE = autovivification      PACKAGE = autovivification
1229
1230 PROTOTYPES: ENABLE
1231
1232 BOOT:
1233 {
1234  if (!a_booted++) {
1235   HV *stash;
1236
1237   a_op_map = ptable_new();
1238 #ifdef USE_ITHREADS
1239   MUTEX_INIT(&a_op_map_mutex);
1240 #endif
1241
1242   PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__);
1243
1244   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
1245   newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));
1246   newCONSTSUB(stash, "A_HINT_WARN",   newSVuv(A_HINT_WARN));
1247   newCONSTSUB(stash, "A_HINT_FETCH",  newSVuv(A_HINT_FETCH));
1248   newCONSTSUB(stash, "A_HINT_STORE",  newSVuv(A_HINT_STORE));
1249   newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS));
1250   newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE));
1251   newCONSTSUB(stash, "A_HINT_MASK",   newSVuv(A_HINT_MASK));
1252   newCONSTSUB(stash, "A_THREADSAFE",  newSVuv(A_THREADSAFE));
1253   newCONSTSUB(stash, "A_FORKSAFE",    newSVuv(A_FORKSAFE));
1254  }
1255
1256  a_setup();
1257 }
1258
1259 #if A_THREADSAFE
1260
1261 void
1262 CLONE(...)
1263 PROTOTYPE: DISABLE
1264 PREINIT:
1265 #if A_WORKAROUND_REQUIRE_PROPAGATION
1266  ptable *t;
1267 #endif
1268  ptable *s;
1269  GV     *gv;
1270 PPCODE:
1271  {
1272   dMY_CXT;
1273 #if A_WORKAROUND_REQUIRE_PROPAGATION
1274   {
1275    a_ptable_clone_ud ud;
1276
1277    t = ptable_new();
1278    a_ptable_clone_ud_init(ud, t, MY_CXT.owner);
1279    ptable_walk(MY_CXT.tbl, a_ptable_clone, &ud);
1280    a_ptable_clone_ud_deinit(ud);
1281   }
1282 #endif
1283   s = ptable_new();
1284  }
1285  {
1286   MY_CXT_CLONE;
1287 #if A_WORKAROUND_REQUIRE_PROPAGATION
1288   MY_CXT.tbl   = t;
1289   MY_CXT.owner = aTHX;
1290 #endif
1291   MY_CXT.seen  = s;
1292  }
1293  gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
1294  if (gv) {
1295   CV *cv = GvCV(gv);
1296   if (!PL_endav)
1297    PL_endav = newAV();
1298   SvREFCNT_inc(cv);
1299   if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
1300    SvREFCNT_dec(cv);
1301   sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &a_endav_vtbl, NULL, 0);
1302  }
1303  XSRETURN(0);
1304
1305 void
1306 _THREAD_CLEANUP(...)
1307 PROTOTYPE: DISABLE
1308 PPCODE:
1309  a_thread_cleanup(aTHX_ NULL);
1310  XSRETURN(0);
1311
1312 #endif /* A_THREADSAFE */
1313
1314 SV *
1315 _tag(SV *hint)
1316 PROTOTYPE: $
1317 CODE:
1318  RETVAL = a_tag(SvOK(hint) ? SvUV(hint) : 0);
1319 OUTPUT:
1320  RETVAL
1321
1322 SV *
1323 _detag(SV *tag)
1324 PROTOTYPE: $
1325 CODE:
1326  if (!SvOK(tag))
1327   XSRETURN_UNDEF;
1328  RETVAL = newSVuv(a_detag(tag));
1329 OUTPUT:
1330  RETVAL