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