]> git.vpit.fr Git - perl/modules/indirect.git/blob - indirect.xs
Rename 'id' to 'hint' for consistency
[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
190  if (*level) {
191   *level = 0;
192   LEAVE;
193   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
194   ENTER;
195  } else {
196   dMY_CXT;
197   PerlMemShared_free(level);
198   ptable_free(MY_CXT.map);
199   ptable_free(MY_CXT.tbl);
200  }
201 }
202
203 STATIC SV *indirect_tag(pTHX_ SV *value) {
204 #define indirect_tag(V) indirect_tag(aTHX_ (V))
205  dMY_CXT;
206
207  value = SvOK(value) && SvROK(value) ? SvRV(value) : NULL;
208  /* We only need for the key to be an unique tag for looking up the value later.
209   * Allocated memory provides convenient unique identifiers, so that's why we
210   * use the value pointer as the key itself. */
211  ptable_store(MY_CXT.tbl, value, value);
212  SvREFCNT_inc(value);
213
214  return newSVuv(PTR2UV(value));
215 }
216
217 STATIC SV *indirect_detag(pTHX_ const SV *hint) {
218 #define indirect_detag(H) indirect_detag(aTHX_ (H))
219  void *tag;
220  SV   *value;
221
222  if (!hint || !SvOK(hint) || !SvIOK(hint))
223   croak("Wrong hint");
224
225  tag = INT2PTR(void *, SvIVX(hint));
226  {
227   dMY_CXT;
228   value = ptable_fetch(MY_CXT.tbl, tag);
229  }
230
231  return value;
232 }
233
234 #else
235
236 STATIC SV *indirect_tag(pTHX_ SV *value) {
237 #define indirect_tag(V) indirect_tag(aTHX_ (V))
238  UV tag = 0;
239
240  if (SvOK(value) && SvROK(value)) {
241   value = SvRV(value);
242   SvREFCNT_inc(value);
243   tag = PTR2UV(value);
244  }
245
246  return newSVuv(tag);
247 }
248
249 #define indirect_detag(H) INT2PTR(SV *, SvUVX(H))
250
251 #endif /* I_THREADSAFE */
252
253 STATIC U32 indirect_hash = 0;
254
255 STATIC SV *indirect_hint(pTHX) {
256 #define indirect_hint() indirect_hint(aTHX)
257  SV *hint;
258 #if I_HAS_PERL(5, 9, 5)
259  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
260                                        NULL,
261                                        __PACKAGE__, __PACKAGE_LEN__,
262                                        0,
263                                        indirect_hash);
264 #else
265  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__,
266                                                                  indirect_hash);
267  if (!val)
268   return 0;
269  hint = *val;
270 #endif
271  return (hint && SvOK(hint)) ? hint : NULL;
272 }
273
274 /* ... op -> source position ............................................... */
275
276 STATIC void indirect_map_store(pTHX_ const OP *o, const char *src, SV *sv) {
277 #define indirect_map_store(O, S, N) indirect_map_store(aTHX_ (O), (S), (N))
278  dMY_CXT;
279  SV *val;
280
281  /* When lex_inwhat is set, we're in a quotelike environment (qq, qr, but not q)
282   * In this case the linestr has temporarly changed, but the old buffer should
283   * still be alive somewhere. */
284
285  if (!PL_lex_inwhat) {
286   const char *pl_linestr = SvPVX_const(PL_linestr);
287   if (MY_CXT.linestr != pl_linestr) {
288    ptable_clear(MY_CXT.map);
289    MY_CXT.linestr = pl_linestr;
290   }
291  }
292
293  val = newSVsv(sv);
294  SvUPGRADE(val, SVt_PVIV);
295  SvUVX(val) = PTR2UV(src);
296  SvIOK_on(val);
297  SvIsUV_on(val);
298  SvREADONLY_on(val);
299
300  ptable_store(MY_CXT.map, o, val);
301 }
302
303 STATIC const char *indirect_map_fetch(pTHX_ const OP *o, SV ** const name) {
304 #define indirect_map_fetch(O, S) indirect_map_fetch(aTHX_ (O), (S))
305  dMY_CXT;
306  SV *val;
307
308  if (MY_CXT.linestr != SvPVX_const(PL_linestr))
309   return NULL;
310
311  val = ptable_fetch(MY_CXT.map, o);
312  if (!val) {
313   *name = NULL;
314   return NULL;
315  }
316
317  *name = val;
318  return INT2PTR(const char *, SvUVX(val));
319 }
320
321 /* --- Check functions ----------------------------------------------------- */
322
323 STATIC const char *indirect_find(pTHX_ SV *sv, const char *s) {
324 #define indirect_find(N, S) indirect_find(aTHX_ (N), (S))
325  STRLEN len;
326  const char *p = NULL, *r = SvPV_const(sv, len);
327
328  if (len >= 1 && *r == '$') {
329   ++r;
330   --len;
331   s = strchr(s, '$');
332   if (!s)
333    return NULL;
334  }
335
336  p = strstr(s, r);
337  while (p) {
338   p += len;
339   if (!isALNUM(*p))
340    break;
341   p = strstr(p + 1, r);
342  }
343
344  return p;
345 }
346
347 /* ... ck_const ............................................................ */
348
349 STATIC OP *(*indirect_old_ck_const)(pTHX_ OP *) = 0;
350
351 STATIC OP *indirect_ck_const(pTHX_ OP *o) {
352  o = CALL_FPTR(indirect_old_ck_const)(aTHX_ o);
353
354  if (indirect_hint()) {
355   SV *sv = cSVOPo_sv;
356   if (SvPOK(sv) && (SvTYPE(sv) >= SVt_PV))
357    indirect_map_store(o, indirect_find(sv, PL_oldbufptr), sv);
358  }
359
360  return o;
361 }
362
363 /* ... ck_rv2sv ............................................................ */
364
365 STATIC OP *(*indirect_old_ck_rv2sv)(pTHX_ OP *) = 0;
366
367 STATIC OP *indirect_ck_rv2sv(pTHX_ OP *o) {
368  if (indirect_hint()) {
369   OP *op = cUNOPo->op_first;
370   SV *sv;
371   const char *name = NULL, *s;
372   STRLEN len;
373   OPCODE type = (OPCODE) op->op_type;
374
375   switch (type) {
376    case OP_GV:
377    case OP_GVSV: {
378     GV *gv = cGVOPx_gv(op);
379     name = GvNAME(gv);
380     len  = GvNAMELEN(gv);
381     break;
382    }
383    default:
384     if ((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP) {
385      SV *nsv = cSVOPx_sv(op);
386      if (SvPOK(nsv) && (SvTYPE(nsv) >= SVt_PV))
387       name = SvPV_const(nsv, len);
388     }
389   }
390   if (!name)
391    goto done;
392
393   sv = sv_2mortal(newSVpvn("$", 1));
394   sv_catpvn_nomg(sv, name, len);
395   s = indirect_find(sv, PL_oldbufptr);
396   if (!s) { /* If it failed, retry without the current stash */
397    const char *stash = HvNAME_get(PL_curstash);
398    STRLEN stashlen = HvNAMELEN_get(PL_curstash);
399
400    if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
401        || name[stashlen] != ':' || name[stashlen+1] != ':') {
402     /* Failed again ? Try to remove main */
403     stash = "main";
404     stashlen = 4;
405     if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
406         || name[stashlen] != ':' || name[stashlen+1] != ':')
407      goto done;
408    }
409
410    sv_setpvn(sv, "$", 1);
411    stashlen += 2;
412    sv_catpvn_nomg(sv, name + stashlen, len - stashlen);
413    s = indirect_find(sv, PL_oldbufptr);
414    if (!s)
415     goto done;
416   }
417
418   o = CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
419   indirect_map_store(o, s, sv);
420   return o;
421  }
422
423 done:
424  return CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
425 }
426
427 /* ... ck_padany ........................................................... */
428
429 STATIC OP *(*indirect_old_ck_padany)(pTHX_ OP *) = 0;
430
431 STATIC OP *indirect_ck_padany(pTHX_ OP *o) {
432  o = CALL_FPTR(indirect_old_ck_padany)(aTHX_ o);
433
434  if (indirect_hint()) {
435   SV *sv;
436   const char *s = PL_oldbufptr, *t = PL_bufptr - 1;
437
438   while (s < t && isSPACE(*s)) ++s;
439   if (*s == '$' && ++s <= t) {
440    while (s < t && isSPACE(*s)) ++s;
441    while (s < t && isSPACE(*t)) --t;
442    sv = sv_2mortal(newSVpvn("$", 1));
443    sv_catpvn_nomg(sv, s, t - s + 1);
444    indirect_map_store(o, s, sv);
445   }
446  }
447
448  return o;
449 }
450
451 /* ... ck_method ........................................................... */
452
453 STATIC OP *(*indirect_old_ck_method)(pTHX_ OP *) = 0;
454
455 STATIC OP *indirect_ck_method(pTHX_ OP *o) {
456  if (indirect_hint()) {
457   OP *op = cUNOPo->op_first;
458   SV *sv;
459   const char *s = indirect_map_fetch(op, &sv);
460   if (!s) {
461    sv = cSVOPx_sv(op);
462    if (!SvPOK(sv) || (SvTYPE(sv) < SVt_PV))
463     goto done;
464    sv = sv_mortalcopy(sv);
465    s  = indirect_find(sv, PL_oldbufptr);
466   }
467   o = CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
468   /* o may now be a method_named */
469   indirect_map_store(o, s, sv);
470   return o;
471  }
472
473 done:
474  return CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
475 }
476
477 /* ... ck_entersub ......................................................... */
478
479 STATIC OP *(*indirect_old_ck_entersub)(pTHX_ OP *) = 0;
480
481 STATIC OP *indirect_ck_entersub(pTHX_ OP *o) {
482  SV *hint = indirect_hint();
483
484  o = CALL_FPTR(indirect_old_ck_entersub)(aTHX_ o);
485
486  if (hint) {
487   const char *mpos, *opos;
488   SV *mnamesv, *onamesv;
489   OP *mop, *oop;
490   LISTOP *lop;
491
492   oop = o;
493   do {
494    lop = (LISTOP *) oop;
495    if (!(lop->op_flags & OPf_KIDS))
496     goto done;
497    oop = lop->op_first;
498   } while (oop->op_type != OP_PUSHMARK);
499   oop = oop->op_sibling;
500   mop = lop->op_last;
501
502   if (mop->op_type == OP_METHOD)
503    mop = cUNOPx(mop)->op_first;
504   else if (mop->op_type != OP_METHOD_NAMED)
505    goto done;
506
507   mpos = indirect_map_fetch(mop, &mnamesv);
508   if (!mpos)
509    goto done;
510
511   opos = indirect_map_fetch(oop, &onamesv);
512   if (!opos)
513    goto done;
514
515   if (mpos < opos) {
516    SV *code = indirect_detag(hint);
517
518    if (code) {
519     SV     *file;
520     line_t  line;
521     dSP;
522
523     ENTER;
524     SAVETMPS;
525
526     onamesv = sv_mortalcopy(onamesv);
527     mnamesv = sv_mortalcopy(mnamesv);
528
529 #ifdef USE_ITHREADS
530     file = sv_2mortal(newSVpv(CopFILE(&PL_compiling), 0));
531 #else
532     file = sv_mortalcopy(CopFILESV(&PL_compiling));
533 #endif
534     line = CopLINE(&PL_compiling);
535
536     PUSHMARK(SP);
537     EXTEND(SP, 4);
538     PUSHs(onamesv);
539     PUSHs(mnamesv);
540     PUSHs(file);
541     mPUSHu(line);
542     PUTBACK;
543
544     call_sv(code, G_VOID);
545
546     PUTBACK;
547
548     FREETMPS;
549     LEAVE;
550    }
551   }
552  }
553
554 done:
555  return o;
556 }
557
558 STATIC U32 indirect_initialized = 0;
559
560 /* --- XS ------------------------------------------------------------------ */
561
562 MODULE = indirect      PACKAGE = indirect
563
564 PROTOTYPES: ENABLE
565
566 BOOT:
567 {
568  if (!indirect_initialized++) {
569   HV *stash;
570
571   MY_CXT_INIT;
572   MY_CXT.map     = ptable_new();
573   MY_CXT.linestr = NULL;
574 #if I_THREADSAFE
575   MY_CXT.tbl     = ptable_new();
576   MY_CXT.owner   = aTHX;
577 #endif
578
579   PERL_HASH(indirect_hash, __PACKAGE__, __PACKAGE_LEN__);
580
581   indirect_old_ck_const    = PL_check[OP_CONST];
582   PL_check[OP_CONST]       = MEMBER_TO_FPTR(indirect_ck_const);
583   indirect_old_ck_rv2sv    = PL_check[OP_RV2SV];
584   PL_check[OP_RV2SV]       = MEMBER_TO_FPTR(indirect_ck_rv2sv);
585   indirect_old_ck_padany   = PL_check[OP_PADANY];
586   PL_check[OP_PADANY]      = MEMBER_TO_FPTR(indirect_ck_padany);
587   indirect_old_ck_method   = PL_check[OP_METHOD];
588   PL_check[OP_METHOD]      = MEMBER_TO_FPTR(indirect_ck_method);
589   indirect_old_ck_entersub = PL_check[OP_ENTERSUB];
590   PL_check[OP_ENTERSUB]    = MEMBER_TO_FPTR(indirect_ck_entersub);
591
592   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
593   newCONSTSUB(stash, "I_THREADSAFE", newSVuv(I_THREADSAFE));
594  }
595 }
596
597 #if I_THREADSAFE
598
599 void
600 CLONE(...)
601 PROTOTYPE: DISABLE
602 PREINIT:
603  ptable *t;
604  int    *level;
605 CODE:
606  {
607   my_cxt_t ud;
608   dMY_CXT;
609   ud.tbl   = t = ptable_new();
610   ud.owner = MY_CXT.owner;
611   ptable_walk(MY_CXT.tbl, indirect_ptable_clone, &ud);
612  }
613  {
614   MY_CXT_CLONE;
615   MY_CXT.map     = ptable_new();
616   MY_CXT.linestr = NULL;
617   MY_CXT.tbl     = t;
618   MY_CXT.owner   = aTHX;
619  }
620  {
621   level = PerlMemShared_malloc(sizeof *level);
622   *level = 1;
623   LEAVE;
624   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
625   ENTER;
626  }
627
628 #endif
629
630 SV *
631 _tag(SV *value)
632 PROTOTYPE: $
633 CODE:
634  RETVAL = indirect_tag(value);
635 OUTPUT:
636  RETVAL