]> git.vpit.fr Git - perl/modules/indirect.git/blob - indirect.xs
Store a proper op info instead of hijacking a SV
[perl/modules/indirect.git] / indirect.xs
1 /* This file is part of the indirect Perl module.
2  * See http://search.cpan.org/dist/indirect/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "indirect"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #ifndef NOOP
15 # define NOOP
16 #endif
17
18 #ifndef dNOOP
19 # define dNOOP
20 #endif
21
22 #ifndef Newx
23 # define Newx(v, n, c) New(0, v, n, c)
24 #endif
25
26 #ifndef SvPV_const
27 # define SvPV_const SvPV
28 #endif
29
30 #ifndef SvPV_nolen_const
31 # define SvPV_nolen_const SvPV_nolen
32 #endif
33
34 #ifndef SvPVX_const
35 # define SvPVX_const SvPVX
36 #endif
37
38 #ifndef sv_catpvn_nomg
39 # define sv_catpvn_nomg sv_catpvn
40 #endif
41
42 #ifndef mPUSHp
43 # define mPUSHp(P, L) PUSHs(sv_2mortal(newSVpvn((P), (L))))
44 #endif
45
46 #ifndef mPUSHu
47 # define mPUSHu(U) PUSHs(sv_2mortal(newSVuv(U)))
48 #endif
49
50 #ifndef HvNAME_get
51 # define HvNAME_get(H) HvNAME(H)
52 #endif
53
54 #ifndef HvNAMELEN_get
55 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
56 #endif
57
58 #define I_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
59
60 #if I_HAS_PERL(5, 10, 0) || defined(PL_parser)
61 # ifndef PL_lex_inwhat
62 #  define PL_lex_inwhat PL_parser->lex_inwhat
63 # endif
64 # ifndef PL_linestr
65 #  define PL_linestr PL_parser->linestr
66 # endif
67 # ifndef PL_bufptr
68 #  define PL_bufptr PL_parser->bufptr
69 # endif
70 # ifndef PL_oldbufptr
71 #  define PL_oldbufptr PL_parser->oldbufptr
72 # endif
73 #else
74 # ifndef PL_lex_inwhat
75 #  define PL_lex_inwhat PL_Ilex_inwhat
76 # endif
77 # ifndef PL_linestr
78 #  define PL_linestr PL_Ilinestr
79 # endif
80 # ifndef PL_bufptr
81 #  define PL_bufptr PL_Ibufptr
82 # endif
83 # ifndef PL_oldbufptr
84 #  define PL_oldbufptr PL_Ioldbufptr
85 # endif
86 #endif
87
88 #ifndef I_WORKAROUND_REQUIRE_PROPAGATION
89 # define I_WORKAROUND_REQUIRE_PROPAGATION !I_HAS_PERL(5, 10, 1)
90 #endif
91
92 /* ... Thread safety and multiplicity ...................................... */
93
94 #ifndef I_MULTIPLICITY
95 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
96 #  define I_MULTIPLICITY 1
97 # else
98 #  define I_MULTIPLICITY 0
99 # endif
100 #endif
101 #if I_MULTIPLICITY && !defined(tTHX)
102 # define tTHX PerlInterpreter*
103 #endif
104
105 #if I_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))
106 # define I_THREADSAFE 1
107 # ifndef MY_CXT_CLONE
108 #  define MY_CXT_CLONE \
109     dMY_CXT_SV;                                                      \
110     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
111     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
112     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
113 # endif
114 #else
115 # define I_THREADSAFE 0
116 # undef  dMY_CXT
117 # define dMY_CXT      dNOOP
118 # undef  MY_CXT
119 # define MY_CXT       indirect_globaldata
120 # undef  START_MY_CXT
121 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
122 # undef  MY_CXT_INIT
123 # define MY_CXT_INIT  NOOP
124 # undef  MY_CXT_CLONE
125 # define MY_CXT_CLONE NOOP
126 #endif
127
128 /* --- Helpers ------------------------------------------------------------- */
129
130 /* ... Thread-safe hints ................................................... */
131
132 /* If any of those are true, we need to store the hint in a global table. */
133
134 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
135
136 typedef struct {
137  SV  *code;
138 #if I_WORKAROUND_REQUIRE_PROPAGATION
139  I32  requires;
140 #endif
141 } indirect_hint_t;
142
143 #define PTABLE_NAME ptable_hints
144
145 #define PTABLE_VAL_FREE(V) \
146    { indirect_hint_t *h = (V); SvREFCNT_dec(h->code); PerlMemShared_free(h); }
147
148 #define pPTBL  pTHX
149 #define pPTBL_ pTHX_
150 #define aPTBL  aTHX
151 #define aPTBL_ aTHX_
152
153 #include "ptable.h"
154
155 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
156 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
157
158 #endif /* I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION */
159
160 /* Define the op->str ptable here because we need to be able to clean it during
161  * thread cleanup. */
162
163 typedef struct {
164  const char *pos;
165  char       *buf;
166  STRLEN      len, size;
167 } indirect_op_info_t;
168
169 #define PTABLE_NAME        ptable
170 #define PTABLE_VAL_FREE(V) if (V) { Safefree(((indirect_op_info_t *) (V))->buf); Safefree(V); }
171
172 #define pPTBL  pTHX
173 #define pPTBL_ pTHX_
174 #define aPTBL  aTHX
175 #define aPTBL_ aTHX_
176
177 #include "ptable.h"
178
179 #define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
180 #define ptable_clear(T)       ptable_clear(aTHX_ (T))
181 #define ptable_free(T)        ptable_free(aTHX_ (T))
182
183 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
184
185 typedef struct {
186 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
187  ptable     *tbl; /* It really is a ptable_hints */
188 #endif
189  ptable     *map;
190  const char *linestr;
191 #if I_THREADSAFE
192  tTHX        owner;
193 #endif
194 } my_cxt_t;
195
196 START_MY_CXT
197
198 #if I_THREADSAFE
199
200 STATIC SV *indirect_clone(pTHX_ SV *sv, tTHX owner) {
201 #define indirect_clone(S, O) indirect_clone(aTHX_ (S), (O))
202  CLONE_PARAMS  param;
203  AV           *stashes = NULL;
204  SV           *dupsv;
205
206  if (SvTYPE(sv) == SVt_PVHV && HvNAME_get(sv))
207   stashes = newAV();
208
209  param.stashes    = stashes;
210  param.flags      = 0;
211  param.proto_perl = owner;
212
213  dupsv = sv_dup(sv, &param);
214
215  if (stashes) {
216   av_undef(stashes);
217   SvREFCNT_dec(stashes);
218  }
219
220  return SvREFCNT_inc(dupsv);
221 }
222
223 STATIC void indirect_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
224  my_cxt_t        *ud = ud_;
225  indirect_hint_t *h1 = ent->val;
226  indirect_hint_t *h2 = PerlMemShared_malloc(sizeof *h2);
227
228  *h2 = *h1;
229
230  if (ud->owner != aTHX)
231   h2->code = indirect_clone(h1->code, ud->owner);
232
233  ptable_hints_store(ud->tbl, ent->key, h2);
234  SvREFCNT_inc(h2->code);
235 }
236
237 STATIC void indirect_thread_cleanup(pTHX_ void *);
238
239 STATIC void indirect_thread_cleanup(pTHX_ void *ud) {
240  int *level = ud;
241
242  if (*level) {
243   *level = 0;
244   LEAVE;
245   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
246   ENTER;
247  } else {
248   dMY_CXT;
249   PerlMemShared_free(level);
250   ptable_free(MY_CXT.map);
251   ptable_hints_free(MY_CXT.tbl);
252  }
253 }
254
255 #endif /* I_THREADSAFE */
256
257 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
258
259 STATIC SV *indirect_tag(pTHX_ SV *value) {
260 #define indirect_tag(V) indirect_tag(aTHX_ (V))
261  indirect_hint_t *h;
262  dMY_CXT;
263
264  value = SvOK(value) && SvROK(value) ? SvRV(value) : NULL;
265
266  h = PerlMemShared_malloc(sizeof *h);
267  h->code = SvREFCNT_inc(value);
268
269 #if I_WORKAROUND_REQUIRE_PROPAGATION
270  {
271   const PERL_SI *si;
272   I32            requires = 0;
273
274   for (si = PL_curstackinfo; si; si = si->si_prev) {
275    I32 cxix;
276
277    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
278     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
279
280     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
281      ++requires;
282    }
283   }
284
285   h->requires = requires;
286  }
287 #endif
288
289  /* We only need for the key to be an unique tag for looking up the value later.
290   * Allocated memory provides convenient unique identifiers, so that's why we
291   * use the value pointer as the key itself. */
292  ptable_hints_store(MY_CXT.tbl, value, h);
293
294  return newSVuv(PTR2UV(value));
295 }
296
297 STATIC SV *indirect_detag(pTHX_ const SV *hint) {
298 #define indirect_detag(H) indirect_detag(aTHX_ (H))
299  indirect_hint_t *h;
300  dMY_CXT;
301
302  if (!(hint && SvOK(hint) && SvIOK(hint)))
303   return NULL;
304
305  h = ptable_fetch(MY_CXT.tbl, INT2PTR(void *, SvUVX(hint)));
306
307 #if I_WORKAROUND_REQUIRE_PROPAGATION
308  {
309   const PERL_SI *si;
310   I32            requires = 0;
311
312   for (si = PL_curstackinfo; si; si = si->si_prev) {
313    I32 cxix;
314
315    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
316     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
317
318     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE
319                                && ++requires > h->requires)
320      return NULL;
321    }
322   }
323  }
324 #endif
325
326  return h->code;
327 }
328
329 #else
330
331 STATIC SV *indirect_tag(pTHX_ SV *value) {
332 #define indirect_tag(V) indirect_tag(aTHX_ (V))
333  UV tag = 0;
334
335  if (SvOK(value) && SvROK(value)) {
336   value = SvRV(value);
337   SvREFCNT_inc(value);
338   tag = PTR2UV(value);
339  }
340
341  return newSVuv(tag);
342 }
343
344 #define indirect_detag(H) (((H) && SvOK(H)) ? INT2PTR(SV *, SvUVX(H)) : NULL)
345
346 #endif /* I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION */
347
348 STATIC U32 indirect_hash = 0;
349
350 STATIC SV *indirect_hint(pTHX) {
351 #define indirect_hint() indirect_hint(aTHX)
352  SV *hint;
353 #if I_HAS_PERL(5, 9, 5)
354  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
355                                        NULL,
356                                        __PACKAGE__, __PACKAGE_LEN__,
357                                        0,
358                                        indirect_hash);
359 #else
360  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__,
361                                                                  indirect_hash);
362  if (!val)
363   return 0;
364  hint = *val;
365 #endif
366  return indirect_detag(hint);
367 }
368
369 /* ... op -> source position ............................................... */
370
371 STATIC void indirect_map_store(pTHX_ const OP *o, const char *src, SV *sv) {
372 #define indirect_map_store(O, S, N) indirect_map_store(aTHX_ (O), (S), (N))
373  indirect_op_info_t *oi;
374  const char *s;
375  STRLEN len;
376  dMY_CXT;
377
378  /* When lex_inwhat is set, we're in a quotelike environment (qq, qr, but not q)
379   * In this case the linestr has temporarly changed, but the old buffer should
380   * still be alive somewhere. */
381
382  if (!PL_lex_inwhat) {
383   const char *pl_linestr = SvPVX_const(PL_linestr);
384   if (MY_CXT.linestr != pl_linestr) {
385    ptable_clear(MY_CXT.map);
386    MY_CXT.linestr = pl_linestr;
387   }
388  }
389
390  if (!(oi = ptable_fetch(MY_CXT.map, o))) {
391   Newx(oi, 1, indirect_op_info_t);
392   ptable_store(MY_CXT.map, o, oi);
393   oi->buf  = NULL;
394   oi->size = 0;
395  }
396
397  s = SvPV_const(sv, len);
398  if (len > oi->size) {
399   Safefree(oi->buf);
400   Newx(oi->buf, len, char);
401   oi->size = len;
402  }
403  Copy(s, oi->buf, len, char);
404  oi->len = len;
405  oi->pos = src;
406 }
407
408 STATIC const indirect_op_info_t *indirect_map_fetch(pTHX_ const OP *o) {
409 #define indirect_map_fetch(O) indirect_map_fetch(aTHX_ (O))
410  const indirect_op_info_t *val;
411  dMY_CXT;
412
413  if (MY_CXT.linestr != SvPVX_const(PL_linestr))
414   return NULL;
415
416  return ptable_fetch(MY_CXT.map, o);
417 }
418
419 STATIC void indirect_map_delete(pTHX_ const OP *o) {
420 #define indirect_map_delete(O) indirect_map_delete(aTHX_ (O))
421  dMY_CXT;
422
423  ptable_store(MY_CXT.map, o, NULL);
424 }
425
426 /* --- Check functions ----------------------------------------------------- */
427
428 STATIC const char *indirect_find(pTHX_ SV *sv, const char *s) {
429 #define indirect_find(N, S) indirect_find(aTHX_ (N), (S))
430  STRLEN len;
431  const char *p = NULL, *r = SvPV_const(sv, len);
432
433  if (len >= 1 && *r == '$') {
434   ++r;
435   --len;
436   s = strchr(s, '$');
437   if (!s)
438    return NULL;
439  }
440
441  p = strstr(s, r);
442  while (p) {
443   p += len;
444   if (!isALNUM(*p))
445    break;
446   p = strstr(p + 1, r);
447  }
448
449  return p;
450 }
451
452 /* ... ck_const ............................................................ */
453
454 STATIC OP *(*indirect_old_ck_const)(pTHX_ OP *) = 0;
455
456 STATIC OP *indirect_ck_const(pTHX_ OP *o) {
457  o = CALL_FPTR(indirect_old_ck_const)(aTHX_ o);
458
459  if (indirect_hint()) {
460   SV *sv = cSVOPo_sv;
461   if (SvPOK(sv) && (SvTYPE(sv) >= SVt_PV)) {
462    indirect_map_store(o, indirect_find(sv, PL_oldbufptr), sv);
463    return o;
464   }
465  }
466
467  indirect_map_delete(o);
468  return o;
469 }
470
471 /* ... ck_rv2sv ............................................................ */
472
473 STATIC OP *(*indirect_old_ck_rv2sv)(pTHX_ OP *) = 0;
474
475 STATIC OP *indirect_ck_rv2sv(pTHX_ OP *o) {
476  if (indirect_hint()) {
477   OP *op = cUNOPo->op_first;
478   SV *sv;
479   const char *name = NULL, *s;
480   STRLEN len;
481   OPCODE type = (OPCODE) op->op_type;
482
483   switch (type) {
484    case OP_GV:
485    case OP_GVSV: {
486     GV *gv = cGVOPx_gv(op);
487     name = GvNAME(gv);
488     len  = GvNAMELEN(gv);
489     break;
490    }
491    default:
492     if ((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP) {
493      SV *nsv = cSVOPx_sv(op);
494      if (SvPOK(nsv) && (SvTYPE(nsv) >= SVt_PV))
495       name = SvPV_const(nsv, len);
496     }
497   }
498   if (!name)
499    goto done;
500
501   sv = sv_2mortal(newSVpvn("$", 1));
502   sv_catpvn_nomg(sv, name, len);
503   s = indirect_find(sv, PL_oldbufptr);
504   if (!s) { /* If it failed, retry without the current stash */
505    const char *stash = HvNAME_get(PL_curstash);
506    STRLEN stashlen = HvNAMELEN_get(PL_curstash);
507
508    if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
509        || name[stashlen] != ':' || name[stashlen+1] != ':') {
510     /* Failed again ? Try to remove main */
511     stash = "main";
512     stashlen = 4;
513     if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
514         || name[stashlen] != ':' || name[stashlen+1] != ':')
515      goto done;
516    }
517
518    sv_setpvn(sv, "$", 1);
519    stashlen += 2;
520    sv_catpvn_nomg(sv, name + stashlen, len - stashlen);
521    s = indirect_find(sv, PL_oldbufptr);
522    if (!s)
523     goto done;
524   }
525
526   o = CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
527   indirect_map_store(o, s, sv);
528   return o;
529  }
530
531 done:
532  o = CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
533
534  indirect_map_delete(o);
535  return o;
536 }
537
538 /* ... ck_padany ........................................................... */
539
540 STATIC OP *(*indirect_old_ck_padany)(pTHX_ OP *) = 0;
541
542 STATIC OP *indirect_ck_padany(pTHX_ OP *o) {
543  o = CALL_FPTR(indirect_old_ck_padany)(aTHX_ o);
544
545  if (indirect_hint()) {
546   SV *sv;
547   const char *s = PL_oldbufptr, *t = PL_bufptr - 1;
548
549   while (s < t && isSPACE(*s)) ++s;
550   if (*s == '$' && ++s <= t) {
551    while (s < t && isSPACE(*s)) ++s;
552    while (s < t && isSPACE(*t)) --t;
553    sv = sv_2mortal(newSVpvn("$", 1));
554    sv_catpvn_nomg(sv, s, t - s + 1);
555    indirect_map_store(o, s, sv);
556    return o;
557   }
558  }
559
560  indirect_map_delete(o);
561  return o;
562 }
563
564 /* ... ck_method ........................................................... */
565
566 STATIC OP *(*indirect_old_ck_method)(pTHX_ OP *) = 0;
567
568 STATIC OP *indirect_ck_method(pTHX_ OP *o) {
569  if (indirect_hint()) {
570   OP *op = cUNOPo->op_first;
571   const indirect_op_info_t *oi = indirect_map_fetch(op);
572   const char *s = NULL;
573   SV *sv;
574
575   if (oi && (s = oi->pos)) {
576    sv = sv_2mortal(newSVpvn(oi->buf, oi->len));
577   } else {
578    sv = cSVOPx_sv(op);
579    if (!SvPOK(sv) || (SvTYPE(sv) < SVt_PV))
580     goto done;
581    sv = sv_mortalcopy(sv);
582    s  = indirect_find(sv, PL_oldbufptr);
583   }
584
585   o = CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
586   /* o may now be a method_named */
587
588   indirect_map_store(o, s, sv);
589   return o;
590  }
591
592 done:
593  o = CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
594
595  indirect_map_delete(o);
596  return o;
597 }
598
599 /* ... ck_entersub ......................................................... */
600
601 STATIC OP *(*indirect_old_ck_entersub)(pTHX_ OP *) = 0;
602
603 STATIC OP *indirect_ck_entersub(pTHX_ OP *o) {
604  SV *code = indirect_hint();
605
606  o = CALL_FPTR(indirect_old_ck_entersub)(aTHX_ o);
607
608  if (code) {
609   const indirect_op_info_t *moi, *ooi;
610   OP     *mop, *oop;
611   LISTOP *lop;
612
613   oop = o;
614   do {
615    lop = (LISTOP *) oop;
616    if (!(lop->op_flags & OPf_KIDS))
617     goto done;
618    oop = lop->op_first;
619   } while (oop->op_type != OP_PUSHMARK);
620   oop = oop->op_sibling;
621   mop = lop->op_last;
622
623   if (!oop)
624    goto done;
625
626   switch (oop->op_type) {
627    case OP_CONST:
628    case OP_RV2SV:
629    case OP_PADSV:
630     break;
631    default:
632     goto done;
633   }
634
635   if (mop->op_type == OP_METHOD)
636    mop = cUNOPx(mop)->op_first;
637   else if (mop->op_type != OP_METHOD_NAMED)
638    goto done;
639
640   moi = indirect_map_fetch(mop);
641   if (!(moi && moi->pos))
642    goto done;
643
644   ooi = indirect_map_fetch(oop);
645   if (!(ooi && ooi->pos))
646    goto done;
647
648   if (moi->pos < ooi->pos) {
649    SV     *file;
650    line_t  line;
651    dSP;
652
653    ENTER;
654    SAVETMPS;
655
656 #ifdef USE_ITHREADS
657    file = sv_2mortal(newSVpv(CopFILE(&PL_compiling), 0));
658 #else
659    file = sv_mortalcopy(CopFILESV(&PL_compiling));
660 #endif
661    line = CopLINE(&PL_compiling);
662
663    PUSHMARK(SP);
664    EXTEND(SP, 4);
665    mPUSHp(ooi->buf, ooi->len);
666    mPUSHp(moi->buf, moi->len);
667    PUSHs(file);
668    mPUSHu(line);
669    PUTBACK;
670
671    call_sv(code, G_VOID);
672
673    PUTBACK;
674
675    FREETMPS;
676    LEAVE;
677   }
678  }
679
680 done:
681  return o;
682 }
683
684 STATIC U32 indirect_initialized = 0;
685
686 /* --- XS ------------------------------------------------------------------ */
687
688 MODULE = indirect      PACKAGE = indirect
689
690 PROTOTYPES: ENABLE
691
692 BOOT:
693 {
694  if (!indirect_initialized++) {
695   HV *stash;
696
697   MY_CXT_INIT;
698   MY_CXT.map     = ptable_new();
699   MY_CXT.linestr = NULL;
700 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
701   MY_CXT.tbl     = ptable_new();
702 #endif
703 #if I_THREADSAFE
704   MY_CXT.owner   = aTHX;
705 #endif
706
707   PERL_HASH(indirect_hash, __PACKAGE__, __PACKAGE_LEN__);
708
709   indirect_old_ck_const    = PL_check[OP_CONST];
710   PL_check[OP_CONST]       = MEMBER_TO_FPTR(indirect_ck_const);
711   indirect_old_ck_rv2sv    = PL_check[OP_RV2SV];
712   PL_check[OP_RV2SV]       = MEMBER_TO_FPTR(indirect_ck_rv2sv);
713   indirect_old_ck_padany   = PL_check[OP_PADANY];
714   PL_check[OP_PADANY]      = MEMBER_TO_FPTR(indirect_ck_padany);
715   indirect_old_ck_method   = PL_check[OP_METHOD];
716   PL_check[OP_METHOD]      = MEMBER_TO_FPTR(indirect_ck_method);
717   indirect_old_ck_entersub = PL_check[OP_ENTERSUB];
718   PL_check[OP_ENTERSUB]    = MEMBER_TO_FPTR(indirect_ck_entersub);
719
720   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
721   newCONSTSUB(stash, "I_THREADSAFE", newSVuv(I_THREADSAFE));
722  }
723 }
724
725 #if I_THREADSAFE
726
727 void
728 CLONE(...)
729 PROTOTYPE: DISABLE
730 PREINIT:
731  ptable *t;
732  int    *level;
733 CODE:
734  {
735   my_cxt_t ud;
736   dMY_CXT;
737   ud.tbl   = t = ptable_new();
738   ud.owner = MY_CXT.owner;
739   ptable_walk(MY_CXT.tbl, indirect_ptable_clone, &ud);
740  }
741  {
742   MY_CXT_CLONE;
743   MY_CXT.map     = ptable_new();
744   MY_CXT.linestr = NULL;
745   MY_CXT.tbl     = t;
746   MY_CXT.owner   = aTHX;
747  }
748  {
749   level = PerlMemShared_malloc(sizeof *level);
750   *level = 1;
751   LEAVE;
752   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
753   ENTER;
754  }
755
756 #endif
757
758 SV *
759 _tag(SV *value)
760 PROTOTYPE: $
761 CODE:
762  RETVAL = indirect_tag(value);
763 OUTPUT:
764  RETVAL