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