]> git.vpit.fr Git - perl/modules/indirect.git/blob - indirect.xs
Replace check functions in a thread-safe way
[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 SvREFCNT_inc_simple_void_NN
39 # ifdef SvREFCNT_inc_simple_NN
40 #  define SvREFCNT_inc_simple_void_NN SvREFCNT_inc_simple_NN
41 # else
42 #  define SvREFCNT_inc_simple_void_NN SvREFCNT_inc
43 # endif
44 #endif
45
46 #ifndef sv_catpvn_nomg
47 # define sv_catpvn_nomg sv_catpvn
48 #endif
49
50 #ifndef mPUSHp
51 # define mPUSHp(P, L) PUSHs(sv_2mortal(newSVpvn((P), (L))))
52 #endif
53
54 #ifndef mPUSHu
55 # define mPUSHu(U) PUSHs(sv_2mortal(newSVuv(U)))
56 #endif
57
58 #ifndef HvNAME_get
59 # define HvNAME_get(H) HvNAME(H)
60 #endif
61
62 #ifndef HvNAMELEN_get
63 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
64 #endif
65
66 #define I_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
67
68 #if I_HAS_PERL(5, 10, 0) || defined(PL_parser)
69 # ifndef PL_linestr
70 #  define PL_linestr PL_parser->linestr
71 # endif
72 # ifndef PL_bufptr
73 #  define PL_bufptr PL_parser->bufptr
74 # endif
75 # ifndef PL_oldbufptr
76 #  define PL_oldbufptr PL_parser->oldbufptr
77 # endif
78 #else
79 # ifndef PL_linestr
80 #  define PL_linestr PL_Ilinestr
81 # endif
82 # ifndef PL_bufptr
83 #  define PL_bufptr PL_Ibufptr
84 # endif
85 # ifndef PL_oldbufptr
86 #  define PL_oldbufptr PL_Ioldbufptr
87 # endif
88 #endif
89
90 #ifndef I_WORKAROUND_REQUIRE_PROPAGATION
91 # define I_WORKAROUND_REQUIRE_PROPAGATION !I_HAS_PERL(5, 10, 1)
92 #endif
93
94 /* ... Thread safety and multiplicity ...................................... */
95
96 /* Safe unless stated otherwise in Makefile.PL */
97 #ifndef I_FORKSAFE
98 # define I_FORKSAFE 1
99 #endif
100
101 #ifndef I_MULTIPLICITY
102 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
103 #  define I_MULTIPLICITY 1
104 # else
105 #  define I_MULTIPLICITY 0
106 # endif
107 #endif
108 #if I_MULTIPLICITY && !defined(tTHX)
109 # define tTHX PerlInterpreter*
110 #endif
111
112 #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))
113 # define I_THREADSAFE 1
114 # ifndef MY_CXT_CLONE
115 #  define MY_CXT_CLONE \
116     dMY_CXT_SV;                                                      \
117     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
118     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
119     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
120 # endif
121 #else
122 # define I_THREADSAFE 0
123 # undef  dMY_CXT
124 # define dMY_CXT      dNOOP
125 # undef  MY_CXT
126 # define MY_CXT       indirect_globaldata
127 # undef  START_MY_CXT
128 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
129 # undef  MY_CXT_INIT
130 # define MY_CXT_INIT  NOOP
131 # undef  MY_CXT_CLONE
132 # define MY_CXT_CLONE NOOP
133 #endif
134
135 #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
136 # define I_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
137 # define I_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
138 #else
139 # define I_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
140 # define I_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
141 #endif
142
143 typedef OP *(*indirect_ck_t)(pTHX_ OP *);
144
145 #ifdef wrap_op_checker
146
147 # define indirect_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
148
149 #else
150
151 STATIC void indirect_ck_replace(pTHX_ OPCODE type, indirect_ck_t new_ck, indirect_ck_t *old_ck_p) {
152 #define indirect_ck_replace(T, NC, OCP) indirect_ck_replace(aTHX_ (T), (NC), (OCP))
153  I_CHECK_MUTEX_LOCK;
154  if (!*old_ck_p) {
155   *old_ck_p      = PL_check[type];
156   PL_check[type] = new_ck;
157  }
158  I_CHECK_MUTEX_UNLOCK;
159 }
160
161 #endif
162
163 STATIC void indirect_ck_restore(pTHX_ OPCODE type, indirect_ck_t *old_ck_p) {
164 #define indirect_ck_restore(T, OCP) indirect_ck_restore(aTHX_ (T), (OCP))
165  I_CHECK_MUTEX_LOCK;
166  if (*old_ck_p) {
167   PL_check[type] = *old_ck_p;
168   *old_ck_p      = 0;
169  }
170  I_CHECK_MUTEX_UNLOCK;
171 }
172
173 /* --- Helpers ------------------------------------------------------------- */
174
175 /* ... Thread-safe hints ................................................... */
176
177 #if I_WORKAROUND_REQUIRE_PROPAGATION
178
179 typedef struct {
180  SV *code;
181  IV  require_tag;
182 } indirect_hint_t;
183
184 #define I_HINT_STRUCT 1
185
186 #define I_HINT_CODE(H) ((H)->code)
187
188 #define I_HINT_FREE(H) {   \
189  indirect_hint_t *h = (H); \
190  SvREFCNT_dec(h->code);    \
191  PerlMemShared_free(h);    \
192 }
193
194 #else  /*  I_WORKAROUND_REQUIRE_PROPAGATION */
195
196 typedef SV indirect_hint_t;
197
198 #define I_HINT_STRUCT 0
199
200 #define I_HINT_CODE(H) (H)
201
202 #define I_HINT_FREE(H) SvREFCNT_dec(H);
203
204 #endif /* !I_WORKAROUND_REQUIRE_PROPAGATION */
205
206 #if I_THREADSAFE
207
208 #define PTABLE_NAME        ptable_hints
209 #define PTABLE_VAL_FREE(V) I_HINT_FREE(V)
210
211 #define pPTBL  pTHX
212 #define pPTBL_ pTHX_
213 #define aPTBL  aTHX
214 #define aPTBL_ aTHX_
215
216 #include "ptable.h"
217
218 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
219 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
220
221 #endif /* I_THREADSAFE */
222
223 /* Define the op->str ptable here because we need to be able to clean it during
224  * thread cleanup. */
225
226 typedef struct {
227  char   *buf;
228  STRLEN  pos;
229  STRLEN  size;
230  STRLEN  len;
231  line_t  line;
232 } indirect_op_info_t;
233
234 #define PTABLE_NAME        ptable
235 #define PTABLE_VAL_FREE(V) if (V) { Safefree(((indirect_op_info_t *) (V))->buf); Safefree(V); }
236
237 #define pPTBL  pTHX
238 #define pPTBL_ pTHX_
239 #define aPTBL  aTHX
240 #define aPTBL_ aTHX_
241
242 #include "ptable.h"
243
244 #define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
245 #define ptable_delete(T, K)   ptable_delete(aTHX_ (T), (K))
246 #define ptable_clear(T)       ptable_clear(aTHX_ (T))
247 #define ptable_free(T)        ptable_free(aTHX_ (T))
248
249 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
250
251 typedef struct {
252 #if I_THREADSAFE
253  ptable *tbl; /* It really is a ptable_hints */
254  tTHX    owner;
255 #endif
256  ptable *map;
257  SV     *global_code;
258 } my_cxt_t;
259
260 START_MY_CXT
261
262 #if I_THREADSAFE
263
264 STATIC SV *indirect_clone(pTHX_ SV *sv, tTHX owner) {
265 #define indirect_clone(S, O) indirect_clone(aTHX_ (S), (O))
266  CLONE_PARAMS  param;
267  AV           *stashes = NULL;
268  SV           *dupsv;
269
270  if (!sv)
271   return NULL;
272
273  if (SvTYPE(sv) == SVt_PVHV && HvNAME_get(sv))
274   stashes = newAV();
275
276  param.stashes    = stashes;
277  param.flags      = 0;
278  param.proto_perl = owner;
279
280  dupsv = sv_dup(sv, &param);
281
282  if (stashes) {
283   av_undef(stashes);
284   SvREFCNT_dec(stashes);
285  }
286
287  return SvREFCNT_inc(dupsv);
288 }
289
290 STATIC void indirect_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
291  my_cxt_t        *ud = ud_;
292  indirect_hint_t *h1 = ent->val;
293  indirect_hint_t *h2;
294
295  if (ud->owner == aTHX)
296   return;
297
298 #if I_HINT_STRUCT
299
300  h2       = PerlMemShared_malloc(sizeof *h2);
301  h2->code = indirect_clone(h1->code, ud->owner);
302 #if I_WORKAROUND_REQUIRE_PROPAGATION
303  h2->require_tag = PTR2IV(indirect_clone(INT2PTR(SV *, h1->require_tag),
304                                          ud->owner));
305 #endif
306
307 #else  /*  I_HINT_STRUCT */
308
309  h2 = indirect_clone(h1, ud->owner);
310
311 #endif /* !I_HINT_STRUCT */
312
313  ptable_hints_store(ud->tbl, ent->key, h2);
314 }
315
316 #include "reap.h"
317
318 STATIC void indirect_thread_cleanup(pTHX_ void *ud) {
319  dMY_CXT;
320
321  SvREFCNT_dec(MY_CXT.global_code);
322  ptable_free(MY_CXT.map);
323  ptable_hints_free(MY_CXT.tbl);
324 }
325
326 #endif /* I_THREADSAFE */
327
328 #if I_WORKAROUND_REQUIRE_PROPAGATION
329 STATIC IV indirect_require_tag(pTHX) {
330 #define indirect_require_tag() indirect_require_tag(aTHX)
331  const CV *cv, *outside;
332
333  cv = PL_compcv;
334
335  if (!cv) {
336   /* If for some reason the pragma is operational at run-time, try to discover
337    * the current cv in use. */
338   const PERL_SI *si;
339
340   for (si = PL_curstackinfo; si; si = si->si_prev) {
341    I32 cxix;
342
343    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
344     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
345
346     switch (CxTYPE(cx)) {
347      case CXt_SUB:
348      case CXt_FORMAT:
349       /* The propagation workaround is only needed up to 5.10.0 and at that
350        * time format and sub contexts were still identical. And even later the
351        * cv members offsets should have been kept the same. */
352       cv = cx->blk_sub.cv;
353       goto get_enclosing_cv;
354      case CXt_EVAL:
355       cv = cx->blk_eval.cv;
356       goto get_enclosing_cv;
357      default:
358       break;
359     }
360    }
361   }
362
363   cv = PL_main_cv;
364  }
365
366 get_enclosing_cv:
367  for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
368   cv = outside;
369
370  return PTR2IV(cv);
371 }
372 #endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
373
374 STATIC SV *indirect_tag(pTHX_ SV *value) {
375 #define indirect_tag(V) indirect_tag(aTHX_ (V))
376  indirect_hint_t *h;
377  SV *code = NULL;
378
379  if (SvROK(value)) {
380   value = SvRV(value);
381   if (SvTYPE(value) >= SVt_PVCV) {
382    code = value;
383    SvREFCNT_inc_simple_void_NN(code);
384   }
385  }
386
387 #if I_HINT_STRUCT
388  h = PerlMemShared_malloc(sizeof *h);
389  h->code        = code;
390 # if I_WORKAROUND_REQUIRE_PROPAGATION
391  h->require_tag = indirect_require_tag();
392 # endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
393 #else  /*  I_HINT_STRUCT */
394  h = code;
395 #endif /* !I_HINT_STRUCT */
396
397 #if I_THREADSAFE
398  {
399   dMY_CXT;
400   /* We only need for the key to be an unique tag for looking up the value later
401    * Allocated memory provides convenient unique identifiers, so that's why we
402    * use the hint as the key itself. */
403   ptable_hints_store(MY_CXT.tbl, h, h);
404  }
405 #endif /* I_THREADSAFE */
406
407  return newSViv(PTR2IV(h));
408 }
409
410 STATIC SV *indirect_detag(pTHX_ const SV *hint) {
411 #define indirect_detag(H) indirect_detag(aTHX_ (H))
412  indirect_hint_t *h;
413 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
414  dMY_CXT;
415 #endif
416
417  h = INT2PTR(indirect_hint_t *, SvIVX(hint));
418 #if I_THREADSAFE
419  h = ptable_fetch(MY_CXT.tbl, h);
420 #endif /* I_THREADSAFE */
421
422 #if I_WORKAROUND_REQUIRE_PROPAGATION
423  if (indirect_require_tag() != h->require_tag)
424   return MY_CXT.global_code;
425 #endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
426
427  return I_HINT_CODE(h);
428 }
429
430 STATIC U32 indirect_hash = 0;
431
432 STATIC SV *indirect_hint(pTHX) {
433 #define indirect_hint() indirect_hint(aTHX)
434  SV *hint = NULL;
435
436  if (IN_PERL_RUNTIME)
437   return NULL;
438
439 #if I_HAS_PERL(5, 10, 0) || defined(PL_parser)
440  if (!PL_parser)
441   return NULL;
442 #endif
443
444 #ifdef cop_hints_fetch_pvn
445  hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__,
446                                                               indirect_hash, 0);
447 #elif I_HAS_PERL(5, 9, 5)
448  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
449                                        NULL,
450                                        __PACKAGE__, __PACKAGE_LEN__,
451                                        0,
452                                        indirect_hash);
453 #else
454  {
455   SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
456   if (val)
457    hint = *val;
458  }
459 #endif
460
461  if (hint && SvIOK(hint))
462   return indirect_detag(hint);
463  else {
464   dMY_CXT;
465   return MY_CXT.global_code;
466  }
467 }
468
469 /* ... op -> source position ............................................... */
470
471 STATIC void indirect_map_store(pTHX_ const OP *o, STRLEN pos, SV *sv, line_t line) {
472 #define indirect_map_store(O, P, N, L) indirect_map_store(aTHX_ (O), (P), (N), (L))
473  indirect_op_info_t *oi;
474  const char *s;
475  STRLEN len;
476  dMY_CXT;
477
478  if (!(oi = ptable_fetch(MY_CXT.map, o))) {
479   Newx(oi, 1, indirect_op_info_t);
480   ptable_store(MY_CXT.map, o, oi);
481   oi->buf  = NULL;
482   oi->size = 0;
483  }
484
485  if (sv) {
486   s = SvPV_const(sv, len);
487  } else {
488   s   = "{";
489   len = 1;
490  }
491
492  if (len > oi->size) {
493   Safefree(oi->buf);
494   Newx(oi->buf, len, char);
495   oi->size = len;
496  }
497  Copy(s, oi->buf, len, char);
498
499  oi->len  = len;
500  oi->pos  = pos;
501  oi->line = line;
502 }
503
504 STATIC const indirect_op_info_t *indirect_map_fetch(pTHX_ const OP *o) {
505 #define indirect_map_fetch(O) indirect_map_fetch(aTHX_ (O))
506  dMY_CXT;
507
508  return ptable_fetch(MY_CXT.map, o);
509 }
510
511 STATIC void indirect_map_delete(pTHX_ const OP *o) {
512 #define indirect_map_delete(O) indirect_map_delete(aTHX_ (O))
513  dMY_CXT;
514
515  ptable_delete(MY_CXT.map, o);
516 }
517
518 /* --- Check functions ----------------------------------------------------- */
519
520 STATIC STRLEN indirect_nextline(const char *s, STRLEN len) {
521  STRLEN i;
522
523  for (i = 0; i < len; ++i) {
524   if (s[i] == '\n') {
525    ++i;
526    while (i < len && s[i] == '\r')
527     ++i;
528    break;
529   }
530  }
531
532  return i;
533 }
534
535 STATIC int indirect_find(pTHX_ SV *name_sv, const char *line_bufptr, STRLEN *name_pos) {
536 #define indirect_find(NSV, LBP, NP) indirect_find(aTHX_ (NSV), (LBP), (NP))
537  STRLEN      name_len, line_len;
538  const char *name, *name_end;
539  const char *line, *line_end;
540  const char *p, *t, *u;
541
542  line     = SvPV_const(PL_linestr, line_len);
543  line_end = line + line_len;
544
545  name = SvPV_const(name_sv, name_len);
546  if (name_len >= 1 && *name == '$') {
547   ++name;
548   --name_len;
549   while (line_bufptr < line_end && *line_bufptr != '$')
550    ++line_bufptr;
551   if (line_bufptr >= line_end)
552    return 0;
553  }
554  name_end = name + name_len;
555
556  p = line_bufptr;
557  while (1) {
558   p = ninstr(p, line_end, name, name_end);
559   if (!p)
560    return 0;
561   if (!isALNUM(p[name_len]))
562    break;
563   /* p points to a word that has name as prefix, skip the rest of the word */
564   p += name_len + 1;
565   while (isALNUM(*p))
566    ++p;
567  }
568
569  t = line;
570  u = t;
571  while (t <= p) {
572   STRLEN i = indirect_nextline(t, line_len);
573   if (i >= line_len)
574    break;
575   u         = t;
576   t        += i;
577   line_len -= i;
578  }
579  *name_pos = p - u;
580
581  return 1;
582 }
583
584 /* ... ck_const ............................................................ */
585
586 STATIC OP *(*indirect_old_ck_const)(pTHX_ OP *) = 0;
587
588 STATIC OP *indirect_ck_const(pTHX_ OP *o) {
589  o = indirect_old_ck_const(aTHX_ o);
590
591  if (indirect_hint()) {
592   SV *sv = cSVOPo_sv;
593
594   if (SvPOK(sv) && (SvTYPE(sv) >= SVt_PV)) {
595    STRLEN pos;
596
597    if (indirect_find(sv, PL_oldbufptr, &pos)) {
598     indirect_map_store(o, pos, sv, CopLINE(&PL_compiling));
599     return o;
600    }
601   }
602  }
603
604  indirect_map_delete(o);
605  return o;
606 }
607
608 /* ... ck_rv2sv ............................................................ */
609
610 STATIC OP *(*indirect_old_ck_rv2sv)(pTHX_ OP *) = 0;
611
612 STATIC OP *indirect_ck_rv2sv(pTHX_ OP *o) {
613  if (indirect_hint()) {
614   OP *op = cUNOPo->op_first;
615   SV *sv;
616   const char *name = NULL;
617   STRLEN pos, len;
618   OPCODE type = (OPCODE) op->op_type;
619
620   switch (type) {
621    case OP_GV:
622    case OP_GVSV: {
623     GV *gv = cGVOPx_gv(op);
624     name = GvNAME(gv);
625     len  = GvNAMELEN(gv);
626     break;
627    }
628    default:
629     if ((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP) {
630      SV *nsv = cSVOPx_sv(op);
631      if (SvPOK(nsv) && (SvTYPE(nsv) >= SVt_PV))
632       name = SvPV_const(nsv, len);
633     }
634   }
635   if (!name)
636    goto done;
637
638   sv = sv_2mortal(newSVpvn("$", 1));
639   sv_catpvn_nomg(sv, name, len);
640   if (!indirect_find(sv, PL_oldbufptr, &pos)) {
641    /* If it failed, retry without the current stash */
642    const char *stash = HvNAME_get(PL_curstash);
643    STRLEN stashlen = HvNAMELEN_get(PL_curstash);
644
645    if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
646        || name[stashlen] != ':' || name[stashlen+1] != ':') {
647     /* Failed again ? Try to remove main */
648     stash = "main";
649     stashlen = 4;
650     if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
651         || name[stashlen] != ':' || name[stashlen+1] != ':')
652      goto done;
653    }
654
655    sv_setpvn(sv, "$", 1);
656    stashlen += 2;
657    sv_catpvn_nomg(sv, name + stashlen, len - stashlen);
658    if (!indirect_find(sv, PL_oldbufptr, &pos))
659     goto done;
660   }
661
662   o = indirect_old_ck_rv2sv(aTHX_ o);
663
664   indirect_map_store(o, pos, sv, CopLINE(&PL_compiling));
665   return o;
666  }
667
668 done:
669  o = indirect_old_ck_rv2sv(aTHX_ o);
670
671  indirect_map_delete(o);
672  return o;
673 }
674
675 /* ... ck_padany ........................................................... */
676
677 STATIC OP *(*indirect_old_ck_padany)(pTHX_ OP *) = 0;
678
679 STATIC OP *indirect_ck_padany(pTHX_ OP *o) {
680  o = indirect_old_ck_padany(aTHX_ o);
681
682  if (indirect_hint()) {
683   SV *sv;
684   const char *s = PL_oldbufptr, *t = PL_bufptr - 1;
685
686   while (s < t && isSPACE(*s)) ++s;
687   if (*s == '$' && ++s <= t) {
688    while (s < t && isSPACE(*s)) ++s;
689    while (s < t && isSPACE(*t)) --t;
690    sv = sv_2mortal(newSVpvn("$", 1));
691    sv_catpvn_nomg(sv, s, t - s + 1);
692    indirect_map_store(o, s - SvPVX_const(PL_linestr),
693                          sv, CopLINE(&PL_compiling));
694    return o;
695   }
696  }
697
698  indirect_map_delete(o);
699  return o;
700 }
701
702 /* ... ck_scope ............................................................ */
703
704 STATIC OP *(*indirect_old_ck_scope)  (pTHX_ OP *) = 0;
705 STATIC OP *(*indirect_old_ck_lineseq)(pTHX_ OP *) = 0;
706
707 STATIC OP *indirect_ck_scope(pTHX_ OP *o) {
708  OP *(*old_ck)(pTHX_ OP *) = 0;
709
710  switch (o->op_type) {
711   case OP_SCOPE:   old_ck = indirect_old_ck_scope;   break;
712   case OP_LINESEQ: old_ck = indirect_old_ck_lineseq; break;
713  }
714  o = old_ck(aTHX_ o);
715
716  if (indirect_hint()) {
717   indirect_map_store(o, PL_oldbufptr - SvPVX_const(PL_linestr),
718                         NULL, CopLINE(&PL_compiling));
719   return o;
720  }
721
722  indirect_map_delete(o);
723  return o;
724 }
725
726 /* We don't need to clean the map entries for leave ops because they can only
727  * be created by mutating from a lineseq. */
728
729 /* ... ck_method ........................................................... */
730
731 STATIC OP *(*indirect_old_ck_method)(pTHX_ OP *) = 0;
732
733 STATIC OP *indirect_ck_method(pTHX_ OP *o) {
734  if (indirect_hint()) {
735   OP *op = cUNOPo->op_first;
736
737   /* Indirect method call is only possible when the method is a bareword, so
738    * don't trip up on $obj->$meth. */
739   if (op && op->op_type == OP_CONST) {
740    const indirect_op_info_t *oi = indirect_map_fetch(op);
741    STRLEN pos;
742    line_t line;
743    SV *sv;
744
745    if (!oi)
746     goto done;
747
748    sv   = sv_2mortal(newSVpvn(oi->buf, oi->len));
749    pos  = oi->pos;
750    /* Keep the old line so that we really point to the first line of the
751     * expression. */
752    line = oi->line;
753
754    o = indirect_old_ck_method(aTHX_ o);
755    /* o may now be a method_named */
756
757    indirect_map_store(o, pos, sv, line);
758    return o;
759   }
760  }
761
762 done:
763  o = indirect_old_ck_method(aTHX_ o);
764
765  indirect_map_delete(o);
766  return o;
767 }
768
769 /* ... ck_method_named ..................................................... */
770
771 /* "use foo/no foo" compiles its call to import/unimport directly to a
772  * method_named op. */
773
774 STATIC OP *(*indirect_old_ck_method_named)(pTHX_ OP *) = 0;
775
776 STATIC OP *indirect_ck_method_named(pTHX_ OP *o) {
777  if (indirect_hint()) {
778   STRLEN pos;
779   line_t line;
780   SV *sv;
781
782   sv = cSVOPo_sv;
783   if (!SvPOK(sv) || (SvTYPE(sv) < SVt_PV))
784    goto done;
785   sv = sv_mortalcopy(sv);
786
787   if (!indirect_find(sv, PL_oldbufptr, &pos))
788    goto done;
789   line = CopLINE(&PL_compiling);
790
791   o = indirect_old_ck_method_named(aTHX_ o);
792
793   indirect_map_store(o, pos, sv, line);
794   return o;
795  }
796
797 done:
798  o = indirect_old_ck_method_named(aTHX_ o);
799
800  indirect_map_delete(o);
801  return o;
802 }
803
804 /* ... ck_entersub ......................................................... */
805
806 STATIC OP *(*indirect_old_ck_entersub)(pTHX_ OP *) = 0;
807
808 STATIC OP *indirect_ck_entersub(pTHX_ OP *o) {
809  SV *code = indirect_hint();
810
811  o = indirect_old_ck_entersub(aTHX_ o);
812
813  if (code) {
814   const indirect_op_info_t *moi, *ooi;
815   OP     *mop, *oop;
816   LISTOP *lop;
817
818   oop = o;
819   do {
820    lop = (LISTOP *) oop;
821    if (!(lop->op_flags & OPf_KIDS))
822     goto done;
823    oop = lop->op_first;
824   } while (oop->op_type != OP_PUSHMARK);
825   oop = oop->op_sibling;
826   mop = lop->op_last;
827
828   if (!oop)
829    goto done;
830
831   switch (oop->op_type) {
832    case OP_CONST:
833    case OP_RV2SV:
834    case OP_PADSV:
835    case OP_SCOPE:
836    case OP_LEAVE:
837     break;
838    default:
839     goto done;
840   }
841
842   if (mop->op_type == OP_METHOD)
843    mop = cUNOPx(mop)->op_first;
844   else if (mop->op_type != OP_METHOD_NAMED)
845    goto done;
846
847   moi = indirect_map_fetch(mop);
848   if (!moi)
849    goto done;
850
851   ooi = indirect_map_fetch(oop);
852   if (!ooi)
853    goto done;
854
855   /* When positions are identical, the method and the object must have the
856    * same name. But it also means that it is an indirect call, as "foo->foo"
857    * results in different positions. */
858   if (   moi->line < ooi->line
859       || (moi->line == ooi->line && moi->pos <= ooi->pos)) {
860    SV *file;
861    dSP;
862
863    ENTER;
864    SAVETMPS;
865
866 #ifdef USE_ITHREADS
867    file = sv_2mortal(newSVpv(CopFILE(&PL_compiling), 0));
868 #else
869    file = sv_mortalcopy(CopFILESV(&PL_compiling));
870 #endif
871
872    PUSHMARK(SP);
873    EXTEND(SP, 4);
874    mPUSHp(ooi->buf, ooi->len);
875    mPUSHp(moi->buf, moi->len);
876    PUSHs(file);
877    mPUSHu(moi->line);
878    PUTBACK;
879
880    call_sv(code, G_VOID);
881
882    PUTBACK;
883
884    FREETMPS;
885    LEAVE;
886   }
887  }
888
889 done:
890  return o;
891 }
892
893 STATIC U32 indirect_initialized = 0;
894
895 STATIC void indirect_teardown(pTHX_ void *root) {
896  if (!indirect_initialized)
897   return;
898
899 #if I_MULTIPLICITY
900  if (aTHX != root)
901   return;
902 #endif
903
904  {
905   dMY_CXT;
906   ptable_free(MY_CXT.map);
907 #if I_THREADSAFE
908   ptable_hints_free(MY_CXT.tbl);
909 #endif
910  }
911
912  indirect_ck_restore(OP_CONST,   &indirect_old_ck_const);
913  indirect_ck_restore(OP_RV2SV,   &indirect_old_ck_rv2sv);
914  indirect_ck_restore(OP_PADANY,  &indirect_old_ck_padany);
915  indirect_ck_restore(OP_SCOPE,   &indirect_old_ck_scope);
916  indirect_ck_restore(OP_LINESEQ, &indirect_old_ck_lineseq);
917
918  indirect_ck_restore(OP_METHOD,       &indirect_old_ck_method);
919  indirect_ck_restore(OP_METHOD_NAMED, &indirect_old_ck_method_named);
920  indirect_ck_restore(OP_ENTERSUB,     &indirect_old_ck_entersub);
921
922  indirect_initialized = 0;
923 }
924
925 STATIC void indirect_setup(pTHX) {
926 #define indirect_setup() indirect_setup(aTHX)
927  if (indirect_initialized)
928   return;
929
930  {
931   MY_CXT_INIT;
932 #if I_THREADSAFE
933   MY_CXT.tbl         = ptable_new();
934   MY_CXT.owner       = aTHX;
935 #endif
936   MY_CXT.map         = ptable_new();
937   MY_CXT.global_code = NULL;
938  }
939
940  indirect_ck_replace(OP_CONST,   indirect_ck_const,  &indirect_old_ck_const);
941  indirect_ck_replace(OP_RV2SV,   indirect_ck_rv2sv,  &indirect_old_ck_rv2sv);
942  indirect_ck_replace(OP_PADANY,  indirect_ck_padany, &indirect_old_ck_padany);
943  indirect_ck_replace(OP_SCOPE,   indirect_ck_scope,  &indirect_old_ck_scope);
944  indirect_ck_replace(OP_LINESEQ, indirect_ck_scope,  &indirect_old_ck_lineseq);
945
946  indirect_ck_replace(OP_METHOD,       indirect_ck_method,
947                                       &indirect_old_ck_method);
948  indirect_ck_replace(OP_METHOD_NAMED, indirect_ck_method_named,
949                                       &indirect_old_ck_method_named);
950  indirect_ck_replace(OP_ENTERSUB,     indirect_ck_entersub,
951                                       &indirect_old_ck_entersub);
952
953 #if I_MULTIPLICITY
954  call_atexit(indirect_teardown, aTHX);
955 #else
956  call_atexit(indirect_teardown, NULL);
957 #endif
958
959  indirect_initialized = 1;
960 }
961
962 STATIC U32 indirect_booted = 0;
963
964 /* --- XS ------------------------------------------------------------------ */
965
966 MODULE = indirect      PACKAGE = indirect
967
968 PROTOTYPES: ENABLE
969
970 BOOT:
971 {
972  if (!indirect_booted++) {
973   HV *stash;
974
975   PERL_HASH(indirect_hash, __PACKAGE__, __PACKAGE_LEN__);
976
977   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
978   newCONSTSUB(stash, "I_THREADSAFE", newSVuv(I_THREADSAFE));
979   newCONSTSUB(stash, "I_FORKSAFE",   newSVuv(I_FORKSAFE));
980  }
981
982  indirect_setup();
983 }
984
985 #if I_THREADSAFE
986
987 void
988 CLONE(...)
989 PROTOTYPE: DISABLE
990 PREINIT:
991  ptable *t;
992  SV     *global_code_dup;
993 PPCODE:
994  {
995   my_cxt_t ud;
996   dMY_CXT;
997   ud.tbl   = t = ptable_new();
998   ud.owner = MY_CXT.owner;
999   ptable_walk(MY_CXT.tbl, indirect_ptable_clone, &ud);
1000   global_code_dup = indirect_clone(MY_CXT.global_code, MY_CXT.owner);
1001  }
1002  {
1003   MY_CXT_CLONE;
1004   MY_CXT.map         = ptable_new();
1005   MY_CXT.tbl         = t;
1006   MY_CXT.owner       = aTHX;
1007   MY_CXT.global_code = global_code_dup;
1008  }
1009  reap(3, indirect_thread_cleanup, NULL);
1010  XSRETURN(0);
1011
1012 #endif
1013
1014 SV *
1015 _tag(SV *value)
1016 PROTOTYPE: $
1017 CODE:
1018  RETVAL = indirect_tag(value);
1019 OUTPUT:
1020  RETVAL
1021
1022 void
1023 _global(SV *code)
1024 PROTOTYPE: $
1025 PPCODE:
1026  if (!SvOK(code))
1027   code = NULL;
1028  else if (SvROK(code))
1029   code = SvRV(code);
1030  {
1031   dMY_CXT;
1032   SvREFCNT_dec(MY_CXT.global_code);
1033   MY_CXT.global_code = SvREFCNT_inc(code);
1034  }
1035  XSRETURN(0);