]> git.vpit.fr Git - perl/modules/Lexical-Types.git/blob - Types.xs
This is 0.16
[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 /* --- XS helpers ---------------------------------------------------------- */
10
11 #define XSH_PACKAGE "Lexical::Types"
12
13 #include "xsh/caps.h"
14 #include "xsh/util.h"
15 #include "xsh/mem.h"
16 #include "xsh/ops.h"
17 #include "xsh/peep.h"
18
19 /* ... Lexical hints ....................................................... */
20
21 #define XSH_HINTS_TYPE_SV 1
22
23 #include "xsh/hints.h"
24
25 #define lt_hint() xsh_hints_detag(xsh_hints_fetch())
26
27 /* ... Thread-local storage ................................................ */
28
29 typedef struct {
30  SV *default_meth;
31 } xsh_user_cxt_t;
32
33 #define XSH_THREADS_COMPILE_TIME_PROTECTION 1
34 #define XSH_THREADS_USER_CLONE_NEEDS_DUP    1
35
36 #if XSH_THREADSAFE
37
38 static void xsh_user_clone(pTHX_ const xsh_user_cxt_t *old_cxt, xsh_user_cxt_t *new_cxt, CLONE_PARAMS *params) {
39  new_cxt->default_meth = xsh_dup_inc(old_cxt->default_meth, params);
40
41  return;
42 }
43
44 #endif /* XSH_THREADSAFE */
45
46 #include "xsh/threads.h"
47
48 /* ... op => info map ...................................................... */
49
50 #define PTABLE_NAME             ptable_map
51 #define PTABLE_VAL_FREE(V)      XSH_SHARED_FREE((V), 0, char)
52 #define PTABLE_VAL_NEED_CONTEXT 0
53 #define PTABLE_NEED_DELETE      1
54 #define PTABLE_NEED_WALK        0
55
56 #include "xsh/ptable.h"
57
58 #define ptable_map_store(T, K, V) ptable_map_store(aPMS_ (T), (K), (V))
59 #define ptable_map_delete(T, K)   ptable_map_delete(aPMS_ (T), (K))
60 #define ptable_map_free(T)        ptable_map_free(aPMS_ (T))
61
62 #ifdef USE_ITHREADS
63
64 static perl_mutex lt_op_map_mutex;
65
66 #endif /* USE_ITHREADS */
67
68 static ptable *lt_op_padxv_map = NULL;
69
70 typedef struct {
71  OP *(*old_pp)(pTHX);
72 #ifdef MULTIPLICITY
73  STRLEN buf_size, orig_pkg_len, type_pkg_len, type_meth_len;
74  char *buf;
75 #else /* MULTIPLICITY */
76  SV *orig_pkg;
77  SV *type_pkg;
78  SV *type_meth;
79 #endif /* !MULTIPLICITY */
80 } lt_op_padxv_info;
81
82 static void lt_op_padxv_info_call(pTHX_ const lt_op_padxv_info *oi, SV *sv) {
83 #define lt_op_padxv_info_call(O, S) lt_op_padxv_info_call(aTHX_ (O), (S))
84  SV *orig_pkg, *type_pkg, *type_meth;
85  int items;
86  dSP;
87
88  ENTER;
89  SAVETMPS;
90
91 #ifdef MULTIPLICITY
92  {
93   STRLEN op_len = oi->orig_pkg_len, tp_len = oi->type_pkg_len;
94   char *buf = oi->buf;
95   orig_pkg  = sv_2mortal(newSVpvn(buf, op_len));
96   SvREADONLY_on(orig_pkg);
97   buf      += op_len;
98   type_pkg  = sv_2mortal(newSVpvn(buf, tp_len));
99   SvREADONLY_on(type_pkg);
100   buf      += tp_len;
101   type_meth = sv_2mortal(newSVpvn(buf, oi->type_meth_len));
102   SvREADONLY_on(type_meth);
103  }
104 #else /* MULTIPLICITY */
105  orig_pkg  = oi->orig_pkg;
106  type_pkg  = oi->type_pkg;
107  type_meth = oi->type_meth;
108 #endif /* !MULTIPLICITY */
109
110  PUSHMARK(SP);
111  EXTEND(SP, 3);
112  PUSHs(type_pkg);
113  PUSHs(sv);
114  PUSHs(orig_pkg);
115  PUTBACK;
116
117  items = call_sv(type_meth, G_ARRAY | G_METHOD);
118
119  SPAGAIN;
120  switch (items) {
121   case 0:
122    break;
123   case 1:
124    sv_setsv(sv, POPs);
125    break;
126   default:
127    croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
128  }
129  PUTBACK;
130
131  FREETMPS;
132  LEAVE;
133
134  return;
135 }
136
137 static void lt_padxv_map_store(pTHX_ const OP *o, SV *orig_pkg, SV *type_pkg, SV *type_meth, OP *(*old_pp)(pTHX)) {
138 #define lt_padxv_map_store(O, OP, TP, TM, PP) lt_padxv_map_store(aTHX_ (O), (OP), (TP), (TM), (PP))
139  lt_op_padxv_info *oi;
140
141  XSH_LOCK(&lt_op_map_mutex);
142
143  if (!(oi = ptable_fetch(lt_op_padxv_map, o))) {
144   XSH_SHARED_ALLOC(oi, 1, lt_op_padxv_info);
145   ptable_map_store(lt_op_padxv_map, o, oi);
146 #ifdef MULTIPLICITY
147   oi->buf      = NULL;
148   oi->buf_size = 0;
149 #else /* MULTIPLICITY */
150  } else {
151   SvREFCNT_dec(oi->orig_pkg);
152   SvREFCNT_dec(oi->type_pkg);
153   SvREFCNT_dec(oi->type_meth);
154 #endif /* !MULTIPLICITY */
155  }
156
157 #ifdef MULTIPLICITY
158  {
159   STRLEN op_len       = SvCUR(orig_pkg);
160   STRLEN tp_len       = SvCUR(type_pkg);
161   STRLEN tm_len       = SvCUR(type_meth);
162   STRLEN new_buf_size = op_len + tp_len + tm_len;
163   char *buf;
164   if (new_buf_size > oi->buf_size) {
165    XSH_SHARED_REALLOC(oi->buf, oi->buf_size, new_buf_size, char);
166    oi->buf_size = new_buf_size;
167   }
168   buf  = oi->buf;
169   Copy(SvPVX(orig_pkg),  buf, op_len, char);
170   buf += op_len;
171   Copy(SvPVX(type_pkg),  buf, tp_len, char);
172   buf += tp_len;
173   Copy(SvPVX(type_meth), buf, tm_len, char);
174   oi->orig_pkg_len  = op_len;
175   oi->type_pkg_len  = tp_len;
176   oi->type_meth_len = tm_len;
177   SvREFCNT_dec(orig_pkg);
178   SvREFCNT_dec(type_pkg);
179   SvREFCNT_dec(type_meth);
180  }
181 #else /* MULTIPLICITY */
182  oi->orig_pkg  = orig_pkg;
183  oi->type_pkg  = type_pkg;
184  oi->type_meth = type_meth;
185 #endif /* !MULTIPLICITY */
186
187  oi->old_pp = old_pp;
188
189  XSH_UNLOCK(&lt_op_map_mutex);
190 }
191
192 static const lt_op_padxv_info *lt_padxv_map_fetch(const OP *o, lt_op_padxv_info *oi) {
193  const lt_op_padxv_info *val;
194
195  XSH_LOCK(&lt_op_map_mutex);
196
197  val = ptable_fetch(lt_op_padxv_map, o);
198  if (val) {
199   *oi = *val;
200   val = oi;
201  }
202
203  XSH_UNLOCK(&lt_op_map_mutex);
204
205  return val;
206 }
207
208 #if XSH_HAS_PERL(5, 17, 6)
209
210 static ptable *lt_op_padrange_map = NULL;
211
212 typedef struct {
213  OP *(*old_pp)(pTHX);
214  const OP *padxv_start;
215 } lt_op_padrange_info;
216
217 static void lt_padrange_map_store(pTHX_ const OP *o, const OP *s, OP *(*old_pp)(pTHX)) {
218 #define lt_padrange_map_store(O, S, PP) lt_padrange_map_store(aTHX_ (O), (S), (PP))
219  lt_op_padrange_info *oi;
220
221  XSH_LOCK(&lt_op_map_mutex);
222
223  if (!(oi = ptable_fetch(lt_op_padrange_map, o))) {
224   XSH_SHARED_ALLOC(oi, 1, lt_op_padrange_info);
225   ptable_map_store(lt_op_padrange_map, o, oi);
226  }
227
228  oi->old_pp      = old_pp;
229  oi->padxv_start = s;
230
231  XSH_UNLOCK(&lt_op_map_mutex);
232 }
233
234 static const lt_op_padrange_info *lt_padrange_map_fetch(const OP *o, lt_op_padrange_info *oi) {
235  const lt_op_padrange_info *val;
236
237  XSH_LOCK(&lt_op_map_mutex);
238
239  val = ptable_fetch(lt_op_padrange_map, o);
240  if (val) {
241   *oi = *val;
242   val = oi;
243  }
244
245  XSH_UNLOCK(&lt_op_map_mutex);
246
247  return val;
248 }
249
250 #endif
251
252 static void lt_map_delete(pTHX_ const OP *o) {
253 #define lt_map_delete(O) lt_map_delete(aTHX_ (O))
254  XSH_LOCK(&lt_op_map_mutex);
255
256  ptable_map_delete(lt_op_padxv_map,    o);
257 #if XSH_HAS_PERL(5, 17, 6)
258  ptable_map_delete(lt_op_padrange_map, o);
259 #endif
260
261  XSH_UNLOCK(&lt_op_map_mutex);
262 }
263
264 /* --- Compatibility wrappers ---------------------------------------------- */
265
266 #if XSH_HAS_PERL(5, 10, 0) || defined(PL_parser)
267 # ifndef PL_in_my_stash
268 #  define PL_in_my_stash PL_parser->in_my_stash
269 # endif
270 #else
271 # ifndef PL_in_my_stash
272 #  define PL_in_my_stash PL_Iin_my_stash
273 # endif
274 #endif
275
276 #ifndef HvNAME_get
277 # define HvNAME_get(H) HvNAME(H)
278 #endif
279
280 #ifndef HvNAMELEN_get
281 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
282 #endif
283
284 #ifndef SvREFCNT_inc_simple_void_NN
285 # define SvREFCNT_inc_simple_void_NN(S) ((void) SvREFCNT_inc(S))
286 #endif
287
288 /* --- PP functions -------------------------------------------------------- */
289
290 /* ... pp_padsv ............................................................ */
291
292 static OP *lt_pp_padsv(pTHX) {
293  lt_op_padxv_info oi;
294
295  if (lt_padxv_map_fetch(PL_op, &oi)) {
296   dTARGET;
297   lt_op_padxv_info_call(&oi, TARG);
298   return oi.old_pp(aTHX);
299  }
300
301  return PL_op->op_ppaddr(aTHX);
302 }
303
304 /* ... pp_padrange (on perl 5.17.6 and above) .............................. */
305
306 #if XSH_HAS_PERL(5, 17, 6)
307
308 static OP *lt_pp_padrange(pTHX) {
309  lt_op_padrange_info roi;
310
311  if (lt_padrange_map_fetch(PL_op, &roi)) {
312   PADOFFSET i, base, count;
313   const OP *p;
314
315   base  = PL_op->op_targ;
316   count = PL_op->op_private & OPpPADRANGE_COUNTMASK;
317
318   for (i = 0, p = roi.padxv_start; i < count && p; ++i, p = p->op_next) {
319    lt_op_padxv_info oi;
320    while (p->op_type == OP_NULL)
321     p = p->op_next;
322    if (p->op_type == OP_PADSV && lt_padxv_map_fetch(p, &oi))
323     lt_op_padxv_info_call(&oi, PAD_SV(base + i));
324   }
325
326   return roi.old_pp(aTHX);
327  }
328
329  return PL_op->op_ppaddr(aTHX);
330 }
331
332 #endif
333
334 /* --- Check functions ----------------------------------------------------- */
335
336 /* ... ck_pad{any,sv} ...................................................... */
337
338 /* Sadly, the padsv OPs we are interested in don't trigger the padsv check
339  * function, but are instead manually mutated from a padany. So we store
340  * the op entry in the op map in the padany check function, and we set their
341  * op_ppaddr member in our peephole optimizer replacement below. */
342
343 static OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0;
344
345 static OP *lt_ck_padany(pTHX_ OP *o) {
346  HV *stash;
347  SV *code;
348
349  o = lt_old_ck_padany(aTHX_ o);
350
351  stash = PL_in_my_stash;
352  if (stash && (code = lt_hint())) {
353   dXSH_CXT;
354   SV *orig_pkg  = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
355   SV *orig_meth = XSH_CXT.default_meth; /* Guarded by lt_hint() */
356   SV *type_pkg  = NULL;
357   SV *type_meth = NULL;
358   int items;
359
360   dSP;
361
362   SvREADONLY_on(orig_pkg);
363
364   ENTER;
365   SAVETMPS;
366
367   PUSHMARK(SP);
368   EXTEND(SP, 2);
369   PUSHs(orig_pkg);
370   PUSHs(orig_meth);
371   PUTBACK;
372
373   items = call_sv(code, G_ARRAY);
374
375   SPAGAIN;
376   if (items > 2)
377    croak(XSH_PACKAGE " mangler should return zero, one or two scalars, but got %d", items);
378   if (items == 0) {
379    SvREFCNT_dec(orig_pkg);
380    FREETMPS;
381    LEAVE;
382    goto skip;
383   } else {
384    SV *rsv;
385    if (items > 1) {
386     rsv = POPs;
387     if (SvOK(rsv)) {
388      type_meth = newSVsv(rsv);
389      SvREADONLY_on(type_meth);
390     }
391    }
392    rsv = POPs;
393    if (SvOK(rsv)) {
394     type_pkg = newSVsv(rsv);
395     SvREADONLY_on(type_pkg);
396    }
397   }
398   PUTBACK;
399
400   FREETMPS;
401   LEAVE;
402
403   if (!type_pkg) {
404    type_pkg = orig_pkg;
405    SvREFCNT_inc_simple_void_NN(orig_pkg);
406   }
407
408   if (!type_meth) {
409    type_meth = orig_meth;
410    SvREFCNT_inc_simple_void_NN(orig_meth);
411   }
412
413   lt_padxv_map_store(o, orig_pkg, type_pkg, type_meth, o->op_ppaddr);
414  } else {
415 skip:
416   lt_map_delete(o);
417  }
418
419  return o;
420 }
421
422 static OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
423
424 static OP *lt_ck_padsv(pTHX_ OP *o) {
425  lt_map_delete(o);
426
427  return lt_old_ck_padsv(aTHX_ o);
428 }
429
430 /* --- Our peephole optimizer ---------------------------------------------- */
431
432 #if XSH_HAS_PERL(5, 17, 6)
433
434 static int lt_maybe_padrange_setup(pTHX_ OP *o, const OP *start) {
435 #define lt_maybe_padrange_setup(O, S) lt_maybe_padrange_setup(aTHX_ (O), (S))
436  PADOFFSET i, count;
437  const OP *p;
438
439  count = o->op_private & OPpPADRANGE_COUNTMASK;
440
441  for (i = 0, p = start; i < count && p; ++i, p = p->op_next) {
442   if (p->op_type == OP_PADSV) {
443    /* In a padrange sequence, either all lexicals are typed, or none are.
444     * Thus we can stop at the first padsv op. However, note that these
445     * lexicals can need to call different methods in different packages. */
446    XSH_LOCK(&lt_op_map_mutex);
447    if (ptable_fetch(lt_op_padxv_map, p)) {
448     XSH_UNLOCK(&lt_op_map_mutex);
449     lt_padrange_map_store(o, start, o->op_ppaddr);
450     o->op_ppaddr = lt_pp_padrange;
451    } else {
452     XSH_UNLOCK(&lt_op_map_mutex);
453    }
454    return 1;
455   }
456  }
457
458  return 0;
459 }
460
461 #endif
462
463 static void xsh_peep_rec(pTHX_ OP *o, ptable *seen) {
464  for (; o; o = o->op_next) {
465   if (xsh_peep_seen(o, seen))
466    break;
467
468   switch (o->op_type) {
469    case OP_PADSV:
470     if (o->op_ppaddr != lt_pp_padsv && o->op_private & OPpLVAL_INTRO) {
471      lt_op_padxv_info *oi;
472      XSH_LOCK(&lt_op_map_mutex);
473      oi = ptable_fetch(lt_op_padxv_map, o);
474      if (oi) {
475       oi->old_pp   = o->op_ppaddr;
476       o->op_ppaddr = lt_pp_padsv;
477      }
478      XSH_UNLOCK(&lt_op_map_mutex);
479     }
480     break;
481 #if XSH_HAS_PERL(5, 17, 6)
482    case OP_PADRANGE:
483     /* We deal with special padrange ops later, in the aassign op they belong
484      * to. */
485     if (o->op_ppaddr != lt_pp_padrange && o->op_private & OPpLVAL_INTRO
486                                        && !(o->op_flags & OPf_SPECIAL)) {
487      /* A padrange op is guaranteed to have previously been a pushmark.
488       * Moreover, for non-special padrange ops (i.e. that aren't for
489       * my (...) = @_), the first original padxv is its sibling or nephew.
490       */
491      OP *kid = OpSIBLING(o);
492      if (kid->op_type == OP_NULL && kid->op_flags & OPf_KIDS) {
493       kid = kUNOP->op_first;
494       if (kid->op_type == OP_NULL)
495        kid = OpSIBLING(kid);
496      }
497      lt_maybe_padrange_setup(o, kid);
498     }
499     break;
500    case OP_AASSIGN: {
501     OP *op;
502     if (cBINOPo->op_first && cBINOPo->op_first->op_flags & OPf_KIDS
503                           && (op = cUNOPx(cBINOPo->op_first)->op_first)
504                           && op->op_type == OP_PADRANGE
505                           && op->op_ppaddr != lt_pp_padrange
506                           && op->op_private & OPpLVAL_INTRO
507                           && op->op_flags & OPf_SPECIAL) {
508      const OP *start = cUNOPx(cBINOPo->op_last)->op_first;
509      if (start->op_type == OP_PUSHMARK)
510       start = OpSIBLING(start);
511      lt_maybe_padrange_setup(op, start);
512     }
513     break;
514    }
515 #endif
516    default:
517     xsh_peep_maybe_recurse(o, seen);
518     break;
519   }
520  }
521 }
522
523 /* --- Module setup/teardown ----------------------------------------------- */
524
525 static void xsh_user_global_setup(pTHX) {
526  lt_op_padxv_map    = ptable_new(32);
527 #if XSH_HAS_PERL(5, 17, 6)
528  lt_op_padrange_map = ptable_new(32);
529 #endif
530
531 #ifdef USE_ITHREADS
532  MUTEX_INIT(&lt_op_map_mutex);
533 #endif
534
535  xsh_ck_replace(OP_PADANY, lt_ck_padany, &lt_old_ck_padany);
536  xsh_ck_replace(OP_PADSV,  lt_ck_padsv,  &lt_old_ck_padsv);
537
538  return;
539 }
540
541 static void xsh_user_local_setup(pTHX_ xsh_user_cxt_t *cxt) {
542  HV *stash;
543
544  stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
545  newCONSTSUB(stash, "LT_THREADSAFE", newSVuv(XSH_THREADSAFE));
546  newCONSTSUB(stash, "LT_FORKSAFE",   newSVuv(XSH_FORKSAFE));
547
548  cxt->default_meth = newSVpvn("TYPEDSCALAR", 11);
549  SvREADONLY_on(cxt->default_meth);
550
551  return;
552 }
553
554 static void xsh_user_local_teardown(pTHX_ xsh_user_cxt_t *cxt) {
555  SvREFCNT_dec(cxt->default_meth);
556  cxt->default_meth = NULL;
557
558  return;
559 }
560
561 static void xsh_user_global_teardown(pTHX) {
562  xsh_ck_restore(OP_PADANY, &lt_old_ck_padany);
563  xsh_ck_restore(OP_PADSV,  &lt_old_ck_padsv);
564
565  ptable_map_free(lt_op_padxv_map);
566  lt_op_padxv_map    = NULL;
567
568 #if XSH_HAS_PERL(5, 17, 6)
569  ptable_map_free(lt_op_padrange_map);
570  lt_op_padrange_map = NULL;
571 #endif
572
573 #ifdef USE_ITHREADS
574  MUTEX_DESTROY(&lt_op_map_mutex);
575 #endif
576
577  return;
578 }
579
580 /* --- XS ------------------------------------------------------------------ */
581
582 MODULE = Lexical::Types      PACKAGE = Lexical::Types
583
584 PROTOTYPES: ENABLE
585
586 BOOT:
587 {
588  xsh_setup();
589 }
590
591 #if XSH_THREADSAFE
592
593 void
594 CLONE(...)
595 PROTOTYPE: DISABLE
596 PPCODE:
597  xsh_clone();
598  XSRETURN(0);
599
600 #endif
601
602 SV *
603 _tag(SV *code)
604 PROTOTYPE: $
605 CODE:
606  if (!SvOK(code))
607   code = NULL;
608  else if (SvROK(code))
609   code = SvRV(code);
610  RETVAL = xsh_hints_tag(code);
611 OUTPUT:
612  RETVAL