]> git.vpit.fr Git - perl/modules/Lexical-Types.git/blob - Types.xs
Work around the hints propagation in requires on perl <= 5.10.0
[perl/modules/Lexical-Types.git] / Types.xs
1 /* This file is part of the Lexical-Types Perl module.
2  * See http://search.cpan.org/dist/Lexical-Types/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "Lexical::Types"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #define LT_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15
16 #if LT_HAS_PERL(5, 10, 0) || defined(PL_parser)
17 # ifndef PL_in_my_stash
18 #  define PL_in_my_stash PL_parser->in_my_stash
19 # endif
20 #else
21 # ifndef PL_in_my_stash
22 #  define PL_in_my_stash PL_Iin_my_stash
23 # endif
24 #endif
25
26 #ifndef LT_WORKAROUND_REQUIRE_PROPAGATION
27 # define LT_WORKAROUND_REQUIRE_PROPAGATION !LT_HAS_PERL(5, 10, 1)
28 #endif
29
30 #ifndef HvNAME_get
31 # define HvNAME_get(H) HvNAME(H)
32 #endif
33
34 #ifndef HvNAMELEN_get
35 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
36 #endif
37
38 #ifndef SvIS_FREED
39 # define SvIS_FREED(sv) ((sv)->sv_flags == SVTYPEMASK)
40 #endif
41
42 /* ... Thread safety and multiplicity ...................................... */
43
44 #ifndef LT_MULTIPLICITY
45 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
46 #  define LT_MULTIPLICITY 1
47 # else
48 #  define LT_MULTIPLICITY 0
49 # endif
50 #endif
51 #if LT_MULTIPLICITY && !defined(tTHX)
52 # define tTHX PerlInterpreter*
53 #endif
54
55 #if LT_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 LT_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 LT_THREADSAFE 0
66 # undef  dMY_CXT
67 # define dMY_CXT      dNOOP
68 # undef  MY_CXT
69 # define MY_CXT       lt_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 /* --- Helpers ------------------------------------------------------------- */
79
80 /* ... Thread-safe hints ................................................... */
81
82 /* If any of those is true, we need to store the hint in a global table. */
83
84 #if LT_THREADSAFE || LT_WORKAROUND_REQUIRE_PROPAGATION
85
86 typedef struct {
87  SV *code;
88 #if LT_WORKAROUND_REQUIRE_PROPAGATION
89  UV  requires;
90 #endif
91 } lt_hint_t;
92
93 #define PTABLE_NAME        ptable_hints
94 #define PTABLE_VAL_FREE(V) { lt_hint_t *h = (V); SvREFCNT_dec(h->code); PerlMemShared_free(h); }
95
96 #define pPTBL  pTHX
97 #define pPTBL_ pTHX_
98 #define aPTBL  aTHX
99 #define aPTBL_ aTHX_
100
101 #include "ptable.h"
102
103 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
104 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
105
106 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
107
108 typedef struct {
109  ptable *tbl; /* It really is a ptable_hints */
110 #if LT_THREADSAFE
111  tTHX    owner;
112 #endif
113 } my_cxt_t;
114
115 START_MY_CXT
116
117 #if LT_THREADSAFE
118
119 STATIC void lt_ptable_hints_clone(pTHX_ ptable_ent *ent, void *ud_) {
120  my_cxt_t  *ud  = ud_;
121  lt_hint_t *h1 = ent->val;
122  lt_hint_t *h2 = PerlMemShared_malloc(sizeof *h2);
123
124  *h2 = *h1;
125
126  if (ud->owner != aTHX) {
127   SV *val = h1->code;
128   CLONE_PARAMS param;
129   AV *stashes = (SvTYPE(val) == SVt_PVHV && HvNAME_get(val)) ? newAV() : NULL;
130   param.stashes    = stashes;
131   param.flags      = 0;
132   param.proto_perl = ud->owner;
133   h2->code = sv_dup(val, &param);
134   if (stashes) {
135    av_undef(stashes);
136    SvREFCNT_dec(stashes);
137   }
138  }
139
140  ptable_hints_store(ud->tbl, ent->key, h2);
141  SvREFCNT_inc(h2->code);
142 }
143
144 STATIC void lt_thread_cleanup(pTHX_ void *);
145
146 STATIC void lt_thread_cleanup(pTHX_ void *ud) {
147  int *level = ud;
148
149  if (*level) {
150   *level = 0;
151   LEAVE;
152   SAVEDESTRUCTOR_X(lt_thread_cleanup, level);
153   ENTER;
154  } else {
155   dMY_CXT;
156   PerlMemShared_free(level);
157   ptable_hints_free(MY_CXT.tbl);
158  }
159 }
160
161 #endif /* LT_THREADSAFE */
162
163 STATIC SV *lt_tag(pTHX_ SV *value) {
164 #define lt_tag(V) lt_tag(aTHX_ (V))
165  lt_hint_t *h;
166  dMY_CXT;
167
168  value = SvOK(value) && SvROK(value) ? SvRV(value) : NULL;
169
170  h = PerlMemShared_malloc(sizeof *h);
171  h->code = SvREFCNT_inc(value);
172
173 #if LT_WORKAROUND_REQUIRE_PROPAGATION
174  {
175   const PERL_SI *si;
176   UV             requires = 0;
177
178   for (si = PL_curstackinfo; si; si = si->si_prev) {
179    I32 cxix;
180
181    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
182     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
183
184     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
185      ++requires;
186    }
187   }
188
189   h->requires = requires;
190  }
191 #endif
192
193  /* We only need for the key to be an unique tag for looking up the value later.
194   * Allocated memory provides convenient unique identifiers, so that's why we
195   * use the value pointer as the key itself. */
196  ptable_hints_store(MY_CXT.tbl, value, h);
197
198  return newSVuv(PTR2UV(value));
199 }
200
201 STATIC SV *lt_detag(pTHX_ const SV *hint) {
202 #define lt_detag(H) lt_detag(aTHX_ (H))
203  lt_hint_t *h;
204  dMY_CXT;
205
206  if (!(hint && SvOK(hint) && SvIOK(hint)))
207   return NULL;
208
209  h = ptable_fetch(MY_CXT.tbl, INT2PTR(void *, SvUVX(hint)));
210
211 #if LT_WORKAROUND_REQUIRE_PROPAGATION
212  {
213   const PERL_SI *si;
214   UV             requires = 0;
215
216   for (si = PL_curstackinfo; si; si = si->si_prev) {
217    I32 cxix;
218
219    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
220     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
221
222     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE
223                                && ++requires > h->requires)
224      return NULL;
225    }
226   }
227  }
228 #endif
229
230  return h->code;
231 }
232
233 #else
234
235 STATIC SV *lt_tag(pTHX_ SV *value) {
236 #define lt_tag(V) lt_tag(aTHX_ (V))
237  UV tag = 0;
238
239  if (SvOK(value) && SvROK(value)) {
240   value = SvRV(value);
241   SvREFCNT_inc(value);
242   tag = PTR2UV(value);
243  }
244
245  return newSVuv(tag);
246 }
247
248 #define lt_detag(H) (((H) && SvOK(H)) ? INT2PTR(SV *, SvUVX(H)) : NULL)
249
250 #endif /* LT_THREADSAFE || LT_WORKAROUND_REQUIRE_PROPAGATION */
251
252 STATIC U32 lt_hash = 0;
253
254 STATIC SV *lt_hint(pTHX) {
255 #define lt_hint() lt_hint(aTHX)
256  SV *hint;
257 #if LT_HAS_PERL(5, 9, 5)
258  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
259                                        NULL,
260                                        __PACKAGE__, __PACKAGE_LEN__,
261                                        0,
262                                        lt_hash);
263 #else
264  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, lt_hash);
265  if (!val)
266   return 0;
267  hint = *val;
268 #endif
269  return lt_detag(hint);
270 }
271
272 /* ... op => info map ...................................................... */
273
274 #define PTABLE_NAME        ptable_map
275 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
276
277 #include "ptable.h"
278
279 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
280 #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
281
282 STATIC ptable *lt_op_map = NULL;
283
284 #ifdef USE_ITHREADS
285 STATIC perl_mutex lt_op_map_mutex;
286 #endif
287
288 typedef struct {
289  SV *orig_pkg;
290  SV *type_pkg;
291  SV *type_meth;
292  OP *(*pp_padsv)(pTHX);
293 } lt_op_info;
294
295 STATIC void lt_map_store(pPTBLMS_ const OP *o, SV *orig_pkg, SV *type_pkg, SV *type_meth, OP *(*pp_padsv)(pTHX)) {
296 #define lt_map_store(O, OP, TP, TM, PP) lt_map_store(aPTBLMS_ (O), (OP), (TP), (TM), (PP))
297  lt_op_info *oi;
298
299 #ifdef USE_ITHREADS
300  MUTEX_LOCK(&lt_op_map_mutex);
301 #endif
302
303  if (!(oi = ptable_fetch(lt_op_map, o))) {
304   oi = PerlMemShared_malloc(sizeof *oi);
305   ptable_map_store(lt_op_map, o, oi);
306  }
307
308  oi->orig_pkg  = orig_pkg;
309  oi->type_pkg  = type_pkg;
310  oi->type_meth = type_meth;
311  oi->pp_padsv  = pp_padsv;
312
313 #ifdef USE_ITHREADS
314  MUTEX_UNLOCK(&lt_op_map_mutex);
315 #endif
316 }
317
318 STATIC const lt_op_info *lt_map_fetch(const OP *o, lt_op_info *oi) {
319  const lt_op_info *val;
320
321 #ifdef USE_ITHREADS
322  MUTEX_LOCK(&lt_op_map_mutex);
323 #endif
324
325  val = ptable_fetch(lt_op_map, o);
326  if (val) {
327   *oi = *val;
328   val = oi;
329  }
330
331 #ifdef USE_ITHREADS
332  MUTEX_UNLOCK(&lt_op_map_mutex);
333 #endif
334
335  return val;
336 }
337
338 STATIC void lt_map_delete(pTHX_ const OP *o) {
339 #define lt_map_delete(O) lt_map_delete(aTHX_ (O))
340 #ifdef USE_ITHREADS
341  MUTEX_LOCK(&lt_op_map_mutex);
342 #endif
343
344  ptable_map_store(lt_op_map, o, NULL);
345
346 #ifdef USE_ITHREADS
347  MUTEX_UNLOCK(&lt_op_map_mutex);
348 #endif
349 }
350
351 /* --- Hooks --------------------------------------------------------------- */
352
353 /* ... Our pp_padsv ........................................................ */
354
355 STATIC OP *lt_pp_padsv(pTHX) {
356  lt_op_info oi;
357
358  if ((PL_op->op_private & OPpLVAL_INTRO) && lt_map_fetch(PL_op, &oi)) {
359   PADOFFSET targ = PL_op->op_targ;
360   SV *sv         = PAD_SVl(targ);
361
362   if (sv) {
363    int items;
364    dSP;
365
366    ENTER;
367    SAVETMPS;
368
369    PUSHMARK(SP);
370    EXTEND(SP, 3);
371    PUSHs(oi.type_pkg);
372    PUSHs(sv);
373    PUSHs(oi.orig_pkg);
374    PUTBACK;
375
376    items = call_sv(oi.type_meth, G_ARRAY | G_METHOD);
377
378    SPAGAIN;
379    switch (items) {
380     case 0:
381      break;
382     case 1:
383      sv_setsv(sv, POPs);
384      break;
385     default:
386      croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
387    }
388    PUTBACK;
389
390    FREETMPS;
391    LEAVE;
392   }
393
394   return CALL_FPTR(oi.pp_padsv)(aTHX);
395  }
396
397  return CALL_FPTR(PL_ppaddr[OP_PADSV])(aTHX);
398 }
399
400 STATIC OP *(*lt_pp_padsv_saved)(pTHX) = 0;
401
402 STATIC void lt_pp_padsv_save(void) {
403  if (lt_pp_padsv_saved)
404   return;
405
406  lt_pp_padsv_saved   = PL_ppaddr[OP_PADSV];
407  PL_ppaddr[OP_PADSV] = lt_pp_padsv;
408 }
409
410 STATIC void lt_pp_padsv_restore(OP *o) {
411  if (!lt_pp_padsv_saved)
412   return;
413
414  if (o->op_ppaddr == lt_pp_padsv)
415   o->op_ppaddr = lt_pp_padsv_saved;
416
417  PL_ppaddr[OP_PADSV] = lt_pp_padsv_saved;
418  lt_pp_padsv_saved   = 0;
419 }
420
421 /* ... Our ck_pad{any,sv} .................................................. */
422
423 /* Sadly, the PADSV OPs we are interested in don't trigger the padsv check
424  * function, but are instead manually mutated from a PADANY. This is why we set
425  * PL_ppaddr[OP_PADSV] in the padany check function so that PADSV OPs will have
426  * their pp_ppaddr set to our pp_padsv. PL_ppaddr[OP_PADSV] is then reset at the
427  * beginning of every ck_pad{any,sv}. Some unwanted OPs can still call our
428  * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV]
429  * globally. */
430
431 STATIC SV *lt_default_meth = NULL;
432
433 STATIC OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0;
434
435 STATIC OP *lt_ck_padany(pTHX_ OP *o) {
436  HV *stash;
437  SV *code;
438
439  lt_pp_padsv_restore(o);
440
441  o = CALL_FPTR(lt_old_ck_padany)(aTHX_ o);
442
443  stash = PL_in_my_stash;
444  if (stash && (code = lt_hint())) {
445   SV *orig_pkg  = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
446   SV *orig_meth = lt_default_meth;
447   SV *type_pkg  = NULL;
448   SV *type_meth = NULL;
449   int items;
450
451   dSP;
452
453   SvREADONLY_on(orig_pkg);
454
455   ENTER;
456   SAVETMPS;
457
458   PUSHMARK(SP);
459   EXTEND(SP, 2);
460   PUSHs(orig_pkg);
461   PUSHs(orig_meth);
462   PUTBACK;
463
464   items = call_sv(code, G_ARRAY);
465
466   SPAGAIN;
467   if (items > 2)
468    croak(__PACKAGE__ " mangler should return zero, one or two scalars, but got %d", items);
469   if (items == 0) {
470    SvREFCNT_dec(orig_pkg);
471    goto skip;
472   } else {
473    SV *rsv;
474    if (items > 1) {
475     rsv = POPs;
476     if (SvOK(rsv)) {
477      type_meth = newSVsv(rsv);
478      SvREADONLY_on(type_meth);
479     }
480    }
481    rsv = POPs;
482    if (SvOK(rsv)) {
483     type_pkg = newSVsv(rsv);
484     SvREADONLY_on(type_pkg);
485    }
486   }
487   PUTBACK;
488
489   FREETMPS;
490   LEAVE;
491
492   if (!type_pkg) {
493    type_pkg = orig_pkg;
494    SvREFCNT_inc(orig_pkg);
495   }
496
497   if (!type_meth) {
498    type_meth = orig_meth;
499    SvREFCNT_inc(orig_meth);
500   }
501
502   lt_pp_padsv_save();
503
504   lt_map_store(o, orig_pkg, type_pkg, type_meth, lt_pp_padsv_saved);
505  } else {
506 skip:
507   lt_map_delete(o);
508  }
509
510  return o;
511 }
512
513 STATIC OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
514
515 STATIC OP *lt_ck_padsv(pTHX_ OP *o) {
516  lt_pp_padsv_restore(o);
517
518  lt_map_delete(o);
519
520  return CALL_FPTR(lt_old_ck_padsv)(aTHX_ o);
521 }
522
523 STATIC U32 lt_initialized = 0;
524
525 /* --- XS ------------------------------------------------------------------ */
526
527 MODULE = Lexical::Types      PACKAGE = Lexical::Types
528
529 PROTOTYPES: ENABLE
530
531 BOOT: 
532 {                                    
533  if (!lt_initialized++) {
534   HV *stash;
535 #if LT_THREADSAFE || LT_WORKAROUND_REQUIRE_PROPAGATION
536   MY_CXT_INIT;
537   MY_CXT.tbl   = ptable_new();
538 #endif
539 #if LT_THREADSAFE
540   MY_CXT.owner = aTHX;
541 #endif
542
543   lt_op_map = ptable_new();
544 #ifdef USE_ITHREADS
545   MUTEX_INIT(&lt_op_map_mutex);
546 #endif
547
548   lt_default_meth = newSVpvn("TYPEDSCALAR", 11);
549   SvREADONLY_on(lt_default_meth);
550
551   PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__);
552
553   lt_old_ck_padany    = PL_check[OP_PADANY];
554   PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_ck_padany);
555   lt_old_ck_padsv     = PL_check[OP_PADSV];
556   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(lt_ck_padsv);
557
558   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
559   newCONSTSUB(stash, "LT_THREADSAFE", newSVuv(LT_THREADSAFE));
560  }
561 }
562
563 #if LT_THREADSAFE
564
565 void
566 CLONE(...)
567 PROTOTYPE: DISABLE
568 PREINIT:
569  ptable *t;
570  int    *level;
571 CODE:
572  {
573   my_cxt_t ud;
574   dMY_CXT;
575   ud.tbl   = t = ptable_new();
576   ud.owner = MY_CXT.owner;
577   ptable_walk(MY_CXT.tbl, lt_ptable_hints_clone, &ud);
578  }
579  {
580   MY_CXT_CLONE;
581   MY_CXT.tbl   = t;
582   MY_CXT.owner = aTHX;
583  }
584  {
585   level = PerlMemShared_malloc(sizeof *level);
586   *level = 1;
587   LEAVE;
588   SAVEDESTRUCTOR_X(lt_thread_cleanup, level);
589   ENTER;
590  }
591
592 #endif
593
594 SV *
595 _tag(SV *value)
596 PROTOTYPE: $
597 CODE:
598  RETVAL = lt_tag(value);
599 OUTPUT:
600  RETVAL