]> git.vpit.fr Git - perl/modules/Lexical-Types.git/blob - Types.xs
Don't cleanup thread local storage before END blocks are executed
[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 LT_HAS_RPEEP
31 # define LT_HAS_RPEEP LT_HAS_PERL(5, 13, 5)
32 #endif
33
34 #ifndef HvNAME_get
35 # define HvNAME_get(H) HvNAME(H)
36 #endif
37
38 #ifndef HvNAMELEN_get
39 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
40 #endif
41
42 #ifndef SvREFCNT_inc_simple_void_NN
43 # define SvREFCNT_inc_simple_void_NN(S) ((void) SvREFCNT_inc(S))
44 #endif
45
46 /* ... Thread safety and multiplicity ...................................... */
47
48 /* Safe unless stated otherwise in Makefile.PL */
49 #ifndef LT_FORKSAFE
50 # define LT_FORKSAFE 1
51 #endif
52
53 #ifndef LT_MULTIPLICITY
54 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
55 #  define LT_MULTIPLICITY 1
56 # else
57 #  define LT_MULTIPLICITY 0
58 # endif
59 #endif
60
61 #ifndef tTHX
62 # define tTHX PerlInterpreter*
63 #endif
64
65 #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))
66 # define LT_THREADSAFE 1
67 # ifndef MY_CXT_CLONE
68 #  define MY_CXT_CLONE \
69     dMY_CXT_SV;                                                      \
70     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
71     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
72     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
73 # endif
74 #else
75 # define LT_THREADSAFE 0
76 # undef  dMY_CXT
77 # define dMY_CXT      dNOOP
78 # undef  MY_CXT
79 # define MY_CXT       lt_globaldata
80 # undef  START_MY_CXT
81 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
82 # undef  MY_CXT_INIT
83 # define MY_CXT_INIT  NOOP
84 # undef  MY_CXT_CLONE
85 # define MY_CXT_CLONE NOOP
86 # undef  pMY_CXT
87 # define pMY_CXT
88 # undef  pMY_CXT_
89 # define pMY_CXT_
90 # undef  aMY_CXT
91 # define aMY_CXT
92 # undef  aMY_CXT_
93 # define aMY_CXT_
94 #endif
95
96 #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
97 # define LT_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
98 # define LT_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
99 #else
100 # define LT_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
101 # define LT_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
102 #endif
103
104 typedef OP *(*lt_ck_t)(pTHX_ OP *);
105
106 #ifdef wrap_op_checker
107
108 # define lt_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
109
110 #else
111
112 STATIC void lt_ck_replace(pTHX_ OPCODE type, lt_ck_t new_ck, lt_ck_t *old_ck_p){
113 #define lt_ck_replace(T, NC, OCP) lt_ck_replace(aTHX_ (T), (NC), (OCP))
114  LT_CHECK_MUTEX_LOCK;
115  if (!*old_ck_p) {
116   *old_ck_p      = PL_check[type];
117   PL_check[type] = new_ck;
118  }
119  LT_CHECK_MUTEX_UNLOCK;
120 }
121
122 #endif
123
124 STATIC void lt_ck_restore(pTHX_ OPCODE type, lt_ck_t *old_ck_p) {
125 #define lt_ck_restore(T, OCP) lt_ck_restore(aTHX_ (T), (OCP))
126  LT_CHECK_MUTEX_LOCK;
127  if (*old_ck_p) {
128   PL_check[type] = *old_ck_p;
129   *old_ck_p      = 0;
130  }
131  LT_CHECK_MUTEX_UNLOCK;
132 }
133
134 /* --- Helpers ------------------------------------------------------------- */
135
136 /* ... Thread-safe hints ................................................... */
137
138 #if LT_WORKAROUND_REQUIRE_PROPAGATION
139
140 typedef struct {
141  SV *code;
142  IV  require_tag;
143 } lt_hint_t;
144
145 #define LT_HINT_STRUCT 1
146
147 #define LT_HINT_CODE(H) ((H)->code)
148
149 #define LT_HINT_FREE(H) { \
150  lt_hint_t *h = (H);      \
151  SvREFCNT_dec(h->code);   \
152  PerlMemShared_free(h);   \
153 }
154
155 #else  /*  LT_WORKAROUND_REQUIRE_PROPAGATION */
156
157 typedef SV lt_hint_t;
158
159 #define LT_HINT_STRUCT 0
160
161 #define LT_HINT_CODE(H) (H)
162
163 #define LT_HINT_FREE(H) SvREFCNT_dec(H);
164
165 #endif /* !LT_WORKAROUND_REQUIRE_PROPAGATION */
166
167 #if LT_THREADSAFE
168
169 #define PTABLE_NAME        ptable_hints
170 #define PTABLE_VAL_FREE(V) LT_HINT_FREE(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_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
180 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
181
182 #endif /* LT_THREADSAFE */
183
184 /* ... "Seen" pointer table ................................................ */
185
186 #define PTABLE_NAME        ptable_seen
187 #define PTABLE_VAL_FREE(V) NOOP
188
189 #include "ptable.h"
190
191 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
192 #define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V))
193 #define ptable_seen_clear(T)       ptable_seen_clear(aPTBLMS_ (T))
194 #define ptable_seen_free(T)        ptable_seen_free(aPTBLMS_ (T))
195
196 /* ... Global data ......................................................... */
197
198 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
199
200 typedef struct {
201 #if LT_THREADSAFE
202  ptable *tbl; /* It really is a ptable_hints */
203  tTHX    owner;
204 #endif
205  ptable *seen; /* It really is a ptable_seen */
206  SV     *default_meth;
207 } my_cxt_t;
208
209 START_MY_CXT
210
211 /* ... Cloning global data ................................................. */
212
213 #if LT_THREADSAFE
214
215 typedef struct {
216  ptable *tbl;
217 #if LT_HAS_PERL(5, 13, 2)
218  CLONE_PARAMS *params;
219 #else
220  CLONE_PARAMS params;
221 #endif
222 } lt_ptable_clone_ud;
223
224 #if LT_HAS_PERL(5, 13, 2)
225 # define lt_ptable_clone_ud_init(U, T, O) \
226    (U).tbl    = (T); \
227    (U).params = Perl_clone_params_new((O), aTHX)
228 # define lt_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
229 # define lt_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), (U)->params))
230 #else
231 # define lt_ptable_clone_ud_init(U, T, O) \
232    (U).tbl               = (T);     \
233    (U).params.stashes    = newAV(); \
234    (U).params.flags      = 0;       \
235    (U).params.proto_perl = (O)
236 # define lt_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
237 # define lt_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), &((U)->params)))
238 #endif
239
240 STATIC void lt_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
241  lt_ptable_clone_ud *ud = ud_;
242  lt_hint_t *h1 = ent->val;
243  lt_hint_t *h2;
244
245 #if LT_HINT_STRUCT
246
247  h2              = PerlMemShared_malloc(sizeof *h2);
248  h2->code        = lt_dup_inc(h1->code, ud);
249 #if LT_WORKAROUND_REQUIRE_PROPAGATION
250  h2->require_tag = PTR2IV(lt_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
251 #endif
252
253 #else /*   LT_HINT_STRUCT */
254
255  h2 = lt_dup_inc(h1, ud);
256
257 #endif /* !LT_HINT_STRUCT */
258
259  ptable_hints_store(ud->tbl, ent->key, h2);
260 }
261
262 STATIC void lt_thread_cleanup(pTHX_ void *ud) {
263  dMY_CXT;
264
265  ptable_hints_free(MY_CXT.tbl);
266  ptable_seen_free(MY_CXT.seen);
267 }
268
269 STATIC int lt_endav_free(pTHX_ SV *sv, MAGIC *mg) {
270  SAVEDESTRUCTOR_X(lt_thread_cleanup, NULL);
271
272  return 0;
273 }
274
275 STATIC MGVTBL lt_endav_vtbl = {
276  0,
277  0,
278  0,
279  0,
280  lt_endav_free
281 #if MGf_COPY
282  , 0
283 #endif
284 #if MGf_DUP
285  , 0
286 #endif
287 #if MGf_LOCAL
288  , 0
289 #endif
290 };
291
292 #endif /* LT_THREADSAFE */
293
294 /* ... Hint tags ........................................................... */
295
296 #if LT_WORKAROUND_REQUIRE_PROPAGATION
297
298 STATIC IV lt_require_tag(pTHX) {
299 #define lt_require_tag() lt_require_tag(aTHX)
300  const CV *cv, *outside;
301
302  cv = PL_compcv;
303
304  if (!cv) {
305   /* If for some reason the pragma is operational at run-time, try to discover
306    * the current cv in use. */
307   const PERL_SI *si;
308
309   for (si = PL_curstackinfo; si; si = si->si_prev) {
310    I32 cxix;
311
312    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
313     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
314
315     switch (CxTYPE(cx)) {
316      case CXt_SUB:
317      case CXt_FORMAT:
318       /* The propagation workaround is only needed up to 5.10.0 and at that
319        * time format and sub contexts were still identical. And even later the
320        * cv members offsets should have been kept the same. */
321       cv = cx->blk_sub.cv;
322       goto get_enclosing_cv;
323      case CXt_EVAL:
324       cv = cx->blk_eval.cv;
325       goto get_enclosing_cv;
326      default:
327       break;
328     }
329    }
330   }
331
332   cv = PL_main_cv;
333  }
334
335 get_enclosing_cv:
336  for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
337   cv = outside;
338
339  return PTR2IV(cv);
340 }
341
342 #endif /* LT_WORKAROUND_REQUIRE_PROPAGATION */
343
344 STATIC SV *lt_tag(pTHX_ SV *value) {
345 #define lt_tag(V) lt_tag(aTHX_ (V))
346  lt_hint_t *h;
347  SV *code = NULL;
348
349  if (SvROK(value)) {
350   value = SvRV(value);
351   if (SvTYPE(value) >= SVt_PVCV) {
352    code = value;
353    SvREFCNT_inc_simple_void_NN(code);
354   }
355  }
356
357 #if LT_HINT_STRUCT
358  h = PerlMemShared_malloc(sizeof *h);
359  h->code        = code;
360 # if LT_WORKAROUND_REQUIRE_PROPAGATION
361  h->require_tag = lt_require_tag();
362 # endif /* LT_WORKAROUND_REQUIRE_PROPAGATION */
363 #else  /*  LT_HINT_STRUCT */
364  h = code;
365 #endif /* !LT_HINT_STRUCT */
366
367 #if LT_THREADSAFE
368  {
369   dMY_CXT;
370   /* We only need for the key to be an unique tag for looking up the value later
371    * Allocated memory provides convenient unique identifiers, so that's why we
372    * use the hint as the key itself. */
373   ptable_hints_store(MY_CXT.tbl, h, h);
374  }
375 #endif /* LT_THREADSAFE */
376
377  return newSViv(PTR2IV(h));
378 }
379
380 STATIC SV *lt_detag(pTHX_ const SV *hint) {
381 #define lt_detag(H) lt_detag(aTHX_ (H))
382  lt_hint_t *h;
383 #if LT_THREADSAFE
384  dMY_CXT;
385 #endif
386
387  if (!(hint && SvIOK(hint)))
388   return NULL;
389
390  h = INT2PTR(lt_hint_t *, SvIVX(hint));
391 #if LT_THREADSAFE
392  h = ptable_fetch(MY_CXT.tbl, h);
393 #endif /* LT_THREADSAFE */
394 #if LT_WORKAROUND_REQUIRE_PROPAGATION
395  if (lt_require_tag() != h->require_tag)
396   return NULL;
397 #endif /* LT_WORKAROUND_REQUIRE_PROPAGATION */
398
399  return LT_HINT_CODE(h);
400 }
401
402 STATIC U32 lt_hash = 0;
403
404 STATIC SV *lt_hint(pTHX) {
405 #define lt_hint() lt_hint(aTHX)
406  SV *hint;
407 #ifdef cop_hints_fetch_pvn
408  hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__, lt_hash,0);
409 #elif LT_HAS_PERL(5, 9, 5)
410  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
411                                        NULL,
412                                        __PACKAGE__, __PACKAGE_LEN__,
413                                        0,
414                                        lt_hash);
415 #else
416  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
417  if (!val)
418   return 0;
419  hint = *val;
420 #endif
421  return lt_detag(hint);
422 }
423
424 /* ... op => info map ...................................................... */
425
426 #define PTABLE_NAME        ptable_map
427 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
428
429 #include "ptable.h"
430
431 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
432 #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
433 #define ptable_map_delete(T, K)   ptable_map_delete(aPTBLMS_ (T), (K))
434
435 #ifdef USE_ITHREADS
436
437 STATIC perl_mutex lt_op_map_mutex;
438
439 #define LT_LOCK(M)   MUTEX_LOCK(M)
440 #define LT_UNLOCK(M) MUTEX_UNLOCK(M)
441
442 #else /* USE_ITHREADS */
443
444 #define LT_LOCK(M)
445 #define LT_UNLOCK(M)
446
447 #endif /* !USE_ITHREADS */
448
449 STATIC ptable *lt_op_padxv_map = NULL;
450
451 typedef struct {
452  OP *(*old_pp)(pTHX);
453 #ifdef MULTIPLICITY
454  STRLEN buf_size, orig_pkg_len, type_pkg_len, type_meth_len;
455  char *buf;
456 #else /* MULTIPLICITY */
457  SV *orig_pkg;
458  SV *type_pkg;
459  SV *type_meth;
460 #endif /* !MULTIPLICITY */
461 } lt_op_padxv_info;
462
463 STATIC void lt_op_padxv_info_call(pTHX_ const lt_op_padxv_info *oi, SV *sv) {
464 #define lt_op_padxv_info_call(O, S) lt_op_padxv_info_call(aTHX_ (O), (S))
465  SV *orig_pkg, *type_pkg, *type_meth;
466  int items;
467  dSP;
468
469  ENTER;
470  SAVETMPS;
471
472 #ifdef MULTIPLICITY
473  {
474   STRLEN op_len = oi->orig_pkg_len, tp_len = oi->type_pkg_len;
475   char *buf = oi->buf;
476   orig_pkg  = sv_2mortal(newSVpvn(buf, op_len));
477   SvREADONLY_on(orig_pkg);
478   buf      += op_len;
479   type_pkg  = sv_2mortal(newSVpvn(buf, tp_len));
480   SvREADONLY_on(type_pkg);
481   buf      += tp_len;
482   type_meth = sv_2mortal(newSVpvn(buf, oi->type_meth_len));
483   SvREADONLY_on(type_meth);
484  }
485 #else /* MULTIPLICITY */
486  orig_pkg  = oi->orig_pkg;
487  type_pkg  = oi->type_pkg;
488  type_meth = oi->type_meth;
489 #endif /* !MULTIPLICITY */
490
491  PUSHMARK(SP);
492  EXTEND(SP, 3);
493  PUSHs(type_pkg);
494  PUSHs(sv);
495  PUSHs(orig_pkg);
496  PUTBACK;
497
498  items = call_sv(type_meth, G_ARRAY | G_METHOD);
499
500  SPAGAIN;
501  switch (items) {
502   case 0:
503    break;
504   case 1:
505    sv_setsv(sv, POPs);
506    break;
507   default:
508    croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
509  }
510  PUTBACK;
511
512  FREETMPS;
513  LEAVE;
514
515  return;
516 }
517
518 STATIC void lt_padxv_map_store(pTHX_ const OP *o, SV *orig_pkg, SV *type_pkg, SV *type_meth, OP *(*old_pp)(pTHX)) {
519 #define lt_padxv_map_store(O, OP, TP, TM, PP) lt_padxv_map_store(aTHX_ (O), (OP), (TP), (TM), (PP))
520  lt_op_padxv_info *oi;
521
522  LT_LOCK(&lt_op_map_mutex);
523
524  if (!(oi = ptable_fetch(lt_op_padxv_map, o))) {
525   oi = PerlMemShared_malloc(sizeof *oi);
526   ptable_map_store(lt_op_padxv_map, o, oi);
527 #ifdef MULTIPLICITY
528   oi->buf      = NULL;
529   oi->buf_size = 0;
530 #else /* MULTIPLICITY */
531  } else {
532   SvREFCNT_dec(oi->orig_pkg);
533   SvREFCNT_dec(oi->type_pkg);
534   SvREFCNT_dec(oi->type_meth);
535 #endif /* !MULTIPLICITY */
536  }
537
538 #ifdef MULTIPLICITY
539  {
540   STRLEN op_len       = SvCUR(orig_pkg);
541   STRLEN tp_len       = SvCUR(type_pkg);
542   STRLEN tm_len       = SvCUR(type_meth);
543   STRLEN new_buf_size = op_len + tp_len + tm_len;
544   char *buf;
545   if (new_buf_size > oi->buf_size) {
546    PerlMemShared_free(oi->buf);
547    oi->buf      = PerlMemShared_malloc(new_buf_size);
548    oi->buf_size = new_buf_size;
549   }
550   buf  = oi->buf;
551   Copy(SvPVX(orig_pkg),  buf, op_len, char);
552   buf += op_len;
553   Copy(SvPVX(type_pkg),  buf, tp_len, char);
554   buf += tp_len;
555   Copy(SvPVX(type_meth), buf, tm_len, char);
556   oi->orig_pkg_len  = op_len;
557   oi->type_pkg_len  = tp_len;
558   oi->type_meth_len = tm_len;
559   SvREFCNT_dec(orig_pkg);
560   SvREFCNT_dec(type_pkg);
561   SvREFCNT_dec(type_meth);
562  }
563 #else /* MULTIPLICITY */
564  oi->orig_pkg  = orig_pkg;
565  oi->type_pkg  = type_pkg;
566  oi->type_meth = type_meth;
567 #endif /* !MULTIPLICITY */
568
569  oi->old_pp = old_pp;
570
571  LT_UNLOCK(&lt_op_map_mutex);
572 }
573
574 STATIC const lt_op_padxv_info *lt_padxv_map_fetch(const OP *o, lt_op_padxv_info *oi) {
575  const lt_op_padxv_info *val;
576
577  LT_LOCK(&lt_op_map_mutex);
578
579  val = ptable_fetch(lt_op_padxv_map, o);
580  if (val) {
581   *oi = *val;
582   val = oi;
583  }
584
585  LT_UNLOCK(&lt_op_map_mutex);
586
587  return val;
588 }
589
590 #if LT_HAS_PERL(5, 17, 6)
591
592 STATIC ptable *lt_op_padrange_map = NULL;
593
594 typedef struct {
595  OP *(*old_pp)(pTHX);
596  const OP *padxv_start;
597 } lt_op_padrange_info;
598
599 STATIC void lt_padrange_map_store(pTHX_ const OP *o, const OP *s, OP *(*old_pp)(pTHX)) {
600 #define lt_padrange_map_store(O, S, PP) lt_padrange_map_store(aTHX_ (O), (S), (PP))
601  lt_op_padrange_info *oi;
602
603  LT_LOCK(&lt_op_map_mutex);
604
605  if (!(oi = ptable_fetch(lt_op_padrange_map, o))) {
606   oi = PerlMemShared_malloc(sizeof *oi);
607   ptable_map_store(lt_op_padrange_map, o, oi);
608  }
609
610  oi->old_pp      = old_pp;
611  oi->padxv_start = s;
612
613  LT_UNLOCK(&lt_op_map_mutex);
614 }
615
616 STATIC const lt_op_padrange_info *lt_padrange_map_fetch(const OP *o, lt_op_padrange_info *oi) {
617  const lt_op_padrange_info *val;
618
619  LT_LOCK(&lt_op_map_mutex);
620
621  val = ptable_fetch(lt_op_padrange_map, o);
622  if (val) {
623   *oi = *val;
624   val = oi;
625  }
626
627  LT_UNLOCK(&lt_op_map_mutex);
628
629  return val;
630 }
631
632 #endif
633
634 STATIC void lt_map_delete(pTHX_ const OP *o) {
635 #define lt_map_delete(O) lt_map_delete(aTHX_ (O))
636  LT_LOCK(&lt_op_map_mutex);
637
638  ptable_map_delete(lt_op_padxv_map,    o);
639 #if LT_HAS_PERL(5, 17, 6)
640  ptable_map_delete(lt_op_padrange_map, o);
641 #endif
642
643  LT_UNLOCK(&lt_op_map_mutex);
644 }
645
646 /* --- Hooks --------------------------------------------------------------- */
647
648 /* ... Our pp_padsv ........................................................ */
649
650 STATIC OP *lt_pp_padsv(pTHX) {
651  lt_op_padxv_info oi;
652
653  if (lt_padxv_map_fetch(PL_op, &oi)) {
654   dTARGET;
655   lt_op_padxv_info_call(&oi, TARG);
656   return oi.old_pp(aTHX);
657  }
658
659  return PL_op->op_ppaddr(aTHX);
660 }
661
662 /* ... Our pp_padrange (on perl 5.17.6 and above) .......................... */
663
664 #if LT_HAS_PERL(5, 17, 6)
665
666 STATIC OP *lt_pp_padrange(pTHX) {
667  lt_op_padrange_info roi;
668
669  if (lt_padrange_map_fetch(PL_op, &roi)) {
670   PADOFFSET i, base, count;
671   const OP *p;
672
673   base  = PL_op->op_targ;
674   count = PL_op->op_private & OPpPADRANGE_COUNTMASK;
675
676   for (i = 0, p = roi.padxv_start; i < count && p; ++i, p = p->op_sibling) {
677    lt_op_padxv_info oi;
678    if (p->op_type == OP_PADSV && lt_padxv_map_fetch(p, &oi))
679     lt_op_padxv_info_call(&oi, PAD_SV(base + i));
680   }
681
682   return roi.old_pp(aTHX);
683  }
684
685  return PL_op->op_ppaddr(aTHX);
686 }
687
688 #endif
689
690 /* ... Our ck_pad{any,sv} .................................................. */
691
692 /* Sadly, the padsv OPs we are interested in don't trigger the padsv check
693  * function, but are instead manually mutated from a padany. So we store
694  * the op entry in the op map in the padany check function, and we set their
695  * op_ppaddr member in our peephole optimizer replacement below. */
696
697 STATIC OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0;
698
699 STATIC OP *lt_ck_padany(pTHX_ OP *o) {
700  HV *stash;
701  SV *code;
702
703  o = lt_old_ck_padany(aTHX_ o);
704
705  stash = PL_in_my_stash;
706  if (stash && (code = lt_hint())) {
707   dMY_CXT;
708   SV *orig_pkg  = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
709   SV *orig_meth = MY_CXT.default_meth;
710   SV *type_pkg  = NULL;
711   SV *type_meth = NULL;
712   int items;
713
714   dSP;
715
716   SvREADONLY_on(orig_pkg);
717
718   ENTER;
719   SAVETMPS;
720
721   PUSHMARK(SP);
722   EXTEND(SP, 2);
723   PUSHs(orig_pkg);
724   PUSHs(orig_meth);
725   PUTBACK;
726
727   items = call_sv(code, G_ARRAY);
728
729   SPAGAIN;
730   if (items > 2)
731    croak(__PACKAGE__ " mangler should return zero, one or two scalars, but got %d", items);
732   if (items == 0) {
733    SvREFCNT_dec(orig_pkg);
734    FREETMPS;
735    LEAVE;
736    goto skip;
737   } else {
738    SV *rsv;
739    if (items > 1) {
740     rsv = POPs;
741     if (SvOK(rsv)) {
742      type_meth = newSVsv(rsv);
743      SvREADONLY_on(type_meth);
744     }
745    }
746    rsv = POPs;
747    if (SvOK(rsv)) {
748     type_pkg = newSVsv(rsv);
749     SvREADONLY_on(type_pkg);
750    }
751   }
752   PUTBACK;
753
754   FREETMPS;
755   LEAVE;
756
757   if (!type_pkg) {
758    type_pkg = orig_pkg;
759    SvREFCNT_inc_simple_void_NN(orig_pkg);
760   }
761
762   if (!type_meth) {
763    type_meth = orig_meth;
764    SvREFCNT_inc_simple_void_NN(orig_meth);
765   }
766
767   lt_padxv_map_store(o, orig_pkg, type_pkg, type_meth, o->op_ppaddr);
768  } else {
769 skip:
770   lt_map_delete(o);
771  }
772
773  return o;
774 }
775
776 STATIC OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
777
778 STATIC OP *lt_ck_padsv(pTHX_ OP *o) {
779  lt_map_delete(o);
780
781  return lt_old_ck_padsv(aTHX_ o);
782 }
783
784 /* ... Our peephole optimizer .............................................. */
785
786 #if LT_HAS_PERL(5, 17, 6)
787
788 STATIC int lt_maybe_padrange_setup(pTHX_ OP *o, const OP *start) {
789 #define lt_maybe_padrange_setup(O, S) lt_maybe_padrange_setup(aTHX_ (O), (S))
790  PADOFFSET i, count;
791  const OP *p;
792
793  count = o->op_private & OPpPADRANGE_COUNTMASK;
794
795  for (i = 0, p = start; i < count && p; ++i, p = p->op_sibling) {
796   if (p->op_type == OP_PADSV) {
797    /* In a padrange sequence, either all lexicals are typed, or none are.
798     * Thus we can stop at the first padsv op. However, note that these
799     * lexicals can need to call different methods in different packages. */
800    LT_LOCK(&lt_op_map_mutex);
801    if (ptable_fetch(lt_op_padxv_map, p)) {
802     LT_UNLOCK(&lt_op_map_mutex);
803     lt_padrange_map_store(o, start, o->op_ppaddr);
804     o->op_ppaddr = lt_pp_padrange;
805    } else {
806     LT_UNLOCK(&lt_op_map_mutex);
807    }
808    return 1;
809   }
810  }
811
812  return 0;
813 }
814
815 #endif
816
817 STATIC peep_t lt_old_peep = 0; /* This is actually the rpeep past 5.13.5 */
818
819 STATIC void lt_peep_rec(pTHX_ OP *o, ptable *seen) {
820 #define lt_peep_rec(O) lt_peep_rec(aTHX_ (O), seen)
821  for (; o; o = o->op_next) {
822   if (ptable_fetch(seen, o))
823    break;
824   ptable_seen_store(seen, o, o);
825
826   switch (o->op_type) {
827    case OP_PADSV:
828     if (o->op_ppaddr != lt_pp_padsv && o->op_private & OPpLVAL_INTRO) {
829      lt_op_padxv_info *oi;
830      LT_LOCK(&lt_op_map_mutex);
831      oi = ptable_fetch(lt_op_padxv_map, o);
832      if (oi) {
833       oi->old_pp   = o->op_ppaddr;
834       o->op_ppaddr = lt_pp_padsv;
835      }
836      LT_UNLOCK(&lt_op_map_mutex);
837     }
838     break;
839 #if LT_HAS_PERL(5, 17, 6)
840    case OP_PADRANGE:
841     /* We deal with special padrange ops later, in the aassign op they belong
842      * to. */
843     if (o->op_ppaddr != lt_pp_padrange && o->op_private & OPpLVAL_INTRO
844                                        && !(o->op_flags & OPf_SPECIAL)) {
845      /* A padrange op is guaranteed to have previously been a pushmark.
846       * Moreover, for non-special padrange ops (i.e. that aren't for
847       * my (...) = @_), the original padxv ops are its siblings. */
848      lt_maybe_padrange_setup(o, o->op_sibling);
849     }
850     break;
851    case OP_AASSIGN: {
852     OP *op;
853     if (cBINOPo->op_first && cBINOPo->op_first->op_flags & OPf_KIDS
854                           && (op = cUNOPx(cBINOPo->op_first)->op_first)
855                           && op->op_type == OP_PADRANGE
856                           && op->op_ppaddr != lt_pp_padrange
857                           && op->op_private & OPpLVAL_INTRO
858                           && op->op_flags & OPf_SPECIAL) {
859      const OP *start = cUNOPx(cBINOPo->op_last)->op_first;
860      if (start->op_type == OP_PUSHMARK)
861       start = start->op_sibling;
862      lt_maybe_padrange_setup(op, start);
863     }
864     break;
865    }
866 #endif
867 #if !LT_HAS_RPEEP
868    case OP_MAPWHILE:
869    case OP_GREPWHILE:
870    case OP_AND:
871    case OP_OR:
872    case OP_ANDASSIGN:
873    case OP_ORASSIGN:
874    case OP_COND_EXPR:
875    case OP_RANGE:
876 # if LT_HAS_PERL(5, 10, 0)
877    case OP_ONCE:
878    case OP_DOR:
879    case OP_DORASSIGN:
880 # endif
881     lt_peep_rec(cLOGOPo->op_other);
882     break;
883    case OP_ENTERLOOP:
884    case OP_ENTERITER:
885     lt_peep_rec(cLOOPo->op_redoop);
886     lt_peep_rec(cLOOPo->op_nextop);
887     lt_peep_rec(cLOOPo->op_lastop);
888     break;
889 # if LT_HAS_PERL(5, 9, 5)
890    case OP_SUBST:
891     lt_peep_rec(cPMOPo->op_pmstashstartu.op_pmreplstart);
892     break;
893 # else
894    case OP_QR:
895    case OP_MATCH:
896    case OP_SUBST:
897     lt_peep_rec(cPMOPo->op_pmreplstart);
898     break;
899 # endif
900 #endif /* !LT_HAS_RPEEP */
901    default:
902     break;
903   }
904  }
905 }
906
907 STATIC void lt_peep(pTHX_ OP *o) {
908  dMY_CXT;
909  ptable *seen = MY_CXT.seen;
910
911  lt_old_peep(aTHX_ o);
912
913  ptable_seen_clear(seen);
914  lt_peep_rec(o);
915  ptable_seen_clear(seen);
916 }
917
918 /* --- Interpreter setup/teardown ------------------------------------------ */
919
920
921 STATIC U32 lt_initialized = 0;
922
923 STATIC void lt_teardown(pTHX_ void *root) {
924  if (!lt_initialized)
925   return;
926
927 #if LT_MULTIPLICITY
928  if (aTHX != root)
929   return;
930 #endif
931
932  {
933   dMY_CXT;
934 #if LT_THREADSAFE
935   ptable_hints_free(MY_CXT.tbl);
936 #endif
937   ptable_seen_free(MY_CXT.seen);
938   SvREFCNT_dec(MY_CXT.default_meth);
939  }
940
941  lt_ck_restore(OP_PADANY, &lt_old_ck_padany);
942  lt_ck_restore(OP_PADSV,  &lt_old_ck_padsv);
943
944 #if LT_HAS_RPEEP
945  PL_rpeepp   = lt_old_peep;
946 #else
947  PL_peepp    = lt_old_peep;
948 #endif
949  lt_old_peep = 0;
950
951  lt_initialized = 0;
952 }
953
954 STATIC void lt_setup(pTHX) {
955 #define lt_setup() lt_setup(aTHX)
956  if (lt_initialized)
957   return;
958
959  {
960   MY_CXT_INIT;
961 #if LT_THREADSAFE
962   MY_CXT.tbl          = ptable_new();
963   MY_CXT.owner        = aTHX;
964 #endif
965   MY_CXT.seen         = ptable_new();
966   MY_CXT.default_meth = newSVpvn("TYPEDSCALAR", 11);
967   SvREADONLY_on(MY_CXT.default_meth);
968  }
969
970  lt_ck_replace(OP_PADANY, lt_ck_padany, &lt_old_ck_padany);
971  lt_ck_replace(OP_PADSV,  lt_ck_padsv,  &lt_old_ck_padsv);
972
973 #if LT_HAS_RPEEP
974  lt_old_peep = PL_rpeepp;
975  PL_rpeepp   = lt_peep;
976 #else
977  lt_old_peep = PL_peepp;
978  PL_peepp    = lt_peep;
979 #endif
980
981 #if LT_MULTIPLICITY
982  call_atexit(lt_teardown, aTHX);
983 #else
984  call_atexit(lt_teardown, NULL);
985 #endif
986
987  lt_initialized = 1;
988 }
989
990 STATIC U32 lt_booted = 0;
991
992 /* --- XS ------------------------------------------------------------------ */
993
994 MODULE = Lexical::Types      PACKAGE = Lexical::Types
995
996 PROTOTYPES: ENABLE
997
998 BOOT:
999 {
1000  if (!lt_booted++) {
1001   HV *stash;
1002
1003   lt_op_padxv_map    = ptable_new();
1004 #if LT_HAS_PERL(5, 17, 6)
1005   lt_op_padrange_map = ptable_new();
1006 #endif
1007 #ifdef USE_ITHREADS
1008   MUTEX_INIT(&lt_op_map_mutex);
1009 #endif
1010
1011   PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__);
1012
1013   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
1014   newCONSTSUB(stash, "LT_THREADSAFE", newSVuv(LT_THREADSAFE));
1015   newCONSTSUB(stash, "LT_FORKSAFE",   newSVuv(LT_FORKSAFE));
1016  }
1017
1018  lt_setup();
1019 }
1020
1021 #if LT_THREADSAFE
1022
1023 void
1024 CLONE(...)
1025 PROTOTYPE: DISABLE
1026 PREINIT:
1027  ptable *t;
1028  ptable *s;
1029  SV     *cloned_default_meth;
1030  GV     *gv;
1031 PPCODE:
1032  {
1033   {
1034    lt_ptable_clone_ud ud;
1035    dMY_CXT;
1036
1037    t = ptable_new();
1038    lt_ptable_clone_ud_init(ud, t, MY_CXT.owner);
1039    ptable_walk(MY_CXT.tbl, lt_ptable_clone, &ud);
1040    cloned_default_meth = lt_dup_inc(MY_CXT.default_meth, &ud);
1041    lt_ptable_clone_ud_deinit(ud);
1042   }
1043   s = ptable_new();
1044  }
1045  {
1046   MY_CXT_CLONE;
1047   MY_CXT.tbl          = t;
1048   MY_CXT.owner        = aTHX;
1049   MY_CXT.seen         = s;
1050   MY_CXT.default_meth = cloned_default_meth;
1051  }
1052  gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
1053  if (gv) {
1054   CV *cv = GvCV(gv);
1055   if (!PL_endav)
1056    PL_endav = newAV();
1057   SvREFCNT_inc(cv);
1058   if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
1059    SvREFCNT_dec(cv);
1060   sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &lt_endav_vtbl, NULL, 0);
1061  }
1062  XSRETURN(0);
1063
1064 void
1065 _THREAD_CLEANUP(...)
1066 PROTOTYPE: DISABLE
1067 PPCODE:
1068  lt_thread_cleanup(aTHX_ NULL);
1069  XSRETURN(0);
1070
1071 #endif
1072
1073 SV *
1074 _tag(SV *value)
1075 PROTOTYPE: $
1076 CODE:
1077  RETVAL = lt_tag(value);
1078 OUTPUT:
1079  RETVAL