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