File Coverage

Types.xs
Criterion Covered Total %
statement 192 199 96.4
branch 90 146 61.6
condition n/a
subroutine n/a
pod n/a
total 282 345 81.7


line stmt bran cond sub pod time code
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 1129           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 1129           dSP;
87              
88 1129           ENTER;
89 1129           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 1129           orig_pkg = oi->orig_pkg;
106 1129           type_pkg = oi->type_pkg;
107 1129           type_meth = oi->type_meth;
108             #endif /* !MULTIPLICITY */
109              
110 1129 50         PUSHMARK(SP);
111 1129 50         EXTEND(SP, 3);
112 1129           PUSHs(type_pkg);
113 1129           PUSHs(sv);
114 1129           PUSHs(orig_pkg);
115 1129           PUTBACK;
116              
117 1129           items = call_sv(type_meth, G_ARRAY | G_METHOD);
118              
119 1126           SPAGAIN;
120 1126           switch (items) {
121             case 0:
122 26           break;
123             case 1:
124 1099           sv_setsv(sv, POPs);
125 1099           break;
126             default:
127 1           croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
128             }
129 1125           PUTBACK;
130              
131 1125 100         FREETMPS;
132 1125           LEAVE;
133              
134 1125           return;
135             }
136              
137 1096           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(<_op_map_mutex);
142              
143 1096 50         if (!(oi = ptable_fetch(lt_op_padxv_map, o))) {
144 1096           XSH_SHARED_ALLOC(oi, 1, lt_op_padxv_info);
145 1096           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 0           SvREFCNT_dec(oi->orig_pkg);
152 0           SvREFCNT_dec(oi->type_pkg);
153 0           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 1096           oi->orig_pkg = orig_pkg;
183 1096           oi->type_pkg = type_pkg;
184 1096           oi->type_meth = type_meth;
185             #endif /* !MULTIPLICITY */
186              
187 1096           oi->old_pp = old_pp;
188              
189             XSH_UNLOCK(<_op_map_mutex);
190 1096           }
191              
192 1129           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(<_op_map_mutex);
196              
197 1129           val = ptable_fetch(lt_op_padxv_map, o);
198 1129 50         if (val) {
199 1129           *oi = *val;
200 1129           val = oi;
201             }
202              
203             XSH_UNLOCK(<_op_map_mutex);
204              
205 1129           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 11           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(<_op_map_mutex);
222              
223 11 50         if (!(oi = ptable_fetch(lt_op_padrange_map, o))) {
224 11           XSH_SHARED_ALLOC(oi, 1, lt_op_padrange_info);
225 11           ptable_map_store(lt_op_padrange_map, o, oi);
226             }
227              
228 11           oi->old_pp = old_pp;
229 11           oi->padxv_start = s;
230              
231             XSH_UNLOCK(<_op_map_mutex);
232 11           }
233              
234 11           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(<_op_map_mutex);
238              
239 11           val = ptable_fetch(lt_op_padrange_map, o);
240 11 50         if (val) {
241 11           *oi = *val;
242 11           val = oi;
243             }
244              
245             XSH_UNLOCK(<_op_map_mutex);
246              
247 11           return val;
248             }
249              
250             #endif
251              
252 7588           static void lt_map_delete(pTHX_ const OP *o) {
253             #define lt_map_delete(O) lt_map_delete(aTHX_ (O))
254             XSH_LOCK(<_op_map_mutex);
255              
256 7588           ptable_map_delete(lt_op_padxv_map, o);
257             #if XSH_HAS_PERL(5, 17, 6)
258 7588           ptable_map_delete(lt_op_padrange_map, o);
259             #endif
260              
261             XSH_UNLOCK(<_op_map_mutex);
262 7588           }
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 1104           static OP *lt_pp_padsv(pTHX) {
293             lt_op_padxv_info oi;
294              
295 1104 50         if (lt_padxv_map_fetch(PL_op, &oi)) {
296 1104           dTARGET;
297 1104           lt_op_padxv_info_call(&oi, TARG);
298 1100           return oi.old_pp(aTHX);
299             }
300              
301 1100           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 11           static OP *lt_pp_padrange(pTHX) {
309             lt_op_padrange_info roi;
310              
311 11 50         if (lt_padrange_map_fetch(PL_op, &roi)) {
312             PADOFFSET i, base, count;
313             const OP *p;
314              
315 11           base = PL_op->op_targ;
316 11           count = PL_op->op_private & OPpPADRANGE_COUNTMASK;
317              
318 36 100         for (i = 0, p = roi.padxv_start; i < count && p; ++i, p = p->op_next) {
    50          
319             lt_op_padxv_info oi;
320 31 100         while (p->op_type == OP_NULL)
321 6           p = p->op_next;
322 25 50         if (p->op_type == OP_PADSV && lt_padxv_map_fetch(p, &oi))
    50          
323 25           lt_op_padxv_info_call(&oi, PAD_SV(base + i));
324             }
325              
326 11           return roi.old_pp(aTHX);
327             }
328              
329 11           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 8676           static OP *lt_ck_padany(pTHX_ OP *o) {
346             HV *stash;
347             SV *code;
348              
349 8676           o = lt_old_ck_padany(aTHX_ o);
350              
351 8676           stash = PL_in_my_stash;
352 9772 100         if (stash && (code = lt_hint())) {
    100          
353             dXSH_CXT;
354 1103 50         SV *orig_pkg = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
355 1103           SV *orig_meth = XSH_CXT.default_meth; /* Guarded by lt_hint() */
356 1103           SV *type_pkg = NULL;
357 1103           SV *type_meth = NULL;
358             int items;
359              
360 1103           dSP;
361              
362 1103           SvREADONLY_on(orig_pkg);
363              
364 1103           ENTER;
365 1103           SAVETMPS;
366              
367 1103 50         PUSHMARK(SP);
368 1103 50         EXTEND(SP, 2);
369 1103           PUSHs(orig_pkg);
370 1103           PUSHs(orig_meth);
371 1103           PUTBACK;
372              
373 1103           items = call_sv(code, G_ARRAY);
374              
375 1100           SPAGAIN;
376 1100 100         if (items > 2)
377 1           croak(XSH_PACKAGE " mangler should return zero, one or two scalars, but got %d", items);
378 1099 100         if (items == 0) {
379 3           SvREFCNT_dec(orig_pkg);
380 3 50         FREETMPS;
381 3           LEAVE;
382 3           goto skip;
383             } else {
384             SV *rsv;
385 1096 100         if (items > 1) {
386 1086           rsv = POPs;
387 1086 100         if (SvOK(rsv)) {
    50          
    50          
388 1085           type_meth = newSVsv(rsv);
389 1085           SvREADONLY_on(type_meth);
390             }
391             }
392 1096           rsv = POPs;
393 1096 100         if (SvOK(rsv)) {
    50          
    50          
394 1095           type_pkg = newSVsv(rsv);
395 1095           SvREADONLY_on(type_pkg);
396             }
397             }
398 1096           PUTBACK;
399              
400 1096 50         FREETMPS;
401 1096           LEAVE;
402              
403 1096 100         if (!type_pkg) {
404 1           type_pkg = orig_pkg;
405 1           SvREFCNT_inc_simple_void_NN(orig_pkg);
406             }
407              
408 1096 100         if (!type_meth) {
409 11           type_meth = orig_meth;
410 11           SvREFCNT_inc_simple_void_NN(orig_meth);
411             }
412              
413 1096           lt_padxv_map_store(o, orig_pkg, type_pkg, type_meth, o->op_ppaddr);
414             } else {
415             skip:
416 7576           lt_map_delete(o);
417             }
418              
419 8672           return o;
420             }
421              
422             static OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
423              
424 12           static OP *lt_ck_padsv(pTHX_ OP *o) {
425 12           lt_map_delete(o);
426              
427 12           return lt_old_ck_padsv(aTHX_ o);
428             }
429              
430             /* --- Our peephole optimizer ---------------------------------------------- */
431              
432             #if XSH_HAS_PERL(5, 17, 6)
433              
434 119           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 119           count = o->op_private & OPpPADRANGE_COUNTMASK;
440              
441 153 100         for (i = 0, p = start; i < count && p; ++i, p = p->op_next) {
    50          
442 119 100         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(<_op_map_mutex);
447 85 100         if (ptable_fetch(lt_op_padxv_map, p)) {
448             XSH_UNLOCK(<_op_map_mutex);
449 11           lt_padrange_map_store(o, start, o->op_ppaddr);
450 11           o->op_ppaddr = lt_pp_padrange;
451             } else {
452             XSH_UNLOCK(<_op_map_mutex);
453             }
454 85           return 1;
455             }
456             }
457              
458 34           return 0;
459             }
460              
461             #endif
462              
463 5688           static void xsh_peep_rec(pTHX_ OP *o, ptable *seen) {
464 102428 100         for (; o; o = o->op_next) {
465 96741 100         if (xsh_peep_seen(o, seen))
466 1           break;
467              
468 96740           switch (o->op_type) {
469             case OP_PADSV:
470 10203 100         if (o->op_ppaddr != lt_pp_padsv && o->op_private & OPpLVAL_INTRO) {
    100          
471             lt_op_padxv_info *oi;
472             XSH_LOCK(<_op_map_mutex);
473 2215           oi = ptable_fetch(lt_op_padxv_map, o);
474 2215 100         if (oi) {
475 1080           oi->old_pp = o->op_ppaddr;
476 1080           o->op_ppaddr = lt_pp_padsv;
477             }
478             XSH_UNLOCK(<_op_map_mutex);
479             }
480 10203           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 153 100         if (o->op_ppaddr != lt_pp_padrange && o->op_private & OPpLVAL_INTRO
    100          
486 119 100         && !(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 28 50         OP *kid = OpSIBLING(o);
492 28 50         if (kid->op_type == OP_NULL && kid->op_flags & OPf_KIDS) {
    0          
493 0           kid = kUNOP->op_first;
494 0 0         if (kid->op_type == OP_NULL)
495 0 0         kid = OpSIBLING(kid);
496             }
497 28           lt_maybe_padrange_setup(o, kid);
498             }
499 153           break;
500             case OP_AASSIGN: {
501             OP *op;
502 560 50         if (cBINOPo->op_first && cBINOPo->op_first->op_flags & OPf_KIDS
    50          
503 560 50         && (op = cUNOPx(cBINOPo->op_first)->op_first)
504 560 100         && op->op_type == OP_PADRANGE
505 91 50         && op->op_ppaddr != lt_pp_padrange
506 91 50         && op->op_private & OPpLVAL_INTRO
507 91 50         && op->op_flags & OPf_SPECIAL) {
508 91           const OP *start = cUNOPx(cBINOPo->op_last)->op_first;
509 91 50         if (start->op_type == OP_PUSHMARK)
510 91 50         start = OpSIBLING(start);
511 91           lt_maybe_padrange_setup(op, start);
512             }
513 560           break;
514             }
515             #endif
516             default:
517             xsh_peep_maybe_recurse(o, seen);
518 85824           break;
519             }
520             }
521 5688           }
522              
523             /* --- Module setup/teardown ----------------------------------------------- */
524              
525 15           static void xsh_user_global_setup(pTHX) {
526 15           lt_op_padxv_map = ptable_new(32);
527             #if XSH_HAS_PERL(5, 17, 6)
528 15           lt_op_padrange_map = ptable_new(32);
529             #endif
530              
531             #ifdef USE_ITHREADS
532             MUTEX_INIT(<_op_map_mutex);
533             #endif
534              
535 15           xsh_ck_replace(OP_PADANY, lt_ck_padany, <_old_ck_padany);
536 15           xsh_ck_replace(OP_PADSV, lt_ck_padsv, <_old_ck_padsv);
537              
538 15           return;
539             }
540              
541 15           static void xsh_user_local_setup(pTHX_ xsh_user_cxt_t *cxt) {
542             HV *stash;
543              
544 15           stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
545 15           newCONSTSUB(stash, "LT_THREADSAFE", newSVuv(XSH_THREADSAFE));
546 15           newCONSTSUB(stash, "LT_FORKSAFE", newSVuv(XSH_FORKSAFE));
547              
548 15           cxt->default_meth = newSVpvn("TYPEDSCALAR", 11);
549 15           SvREADONLY_on(cxt->default_meth);
550              
551 15           return;
552             }
553              
554 15           static void xsh_user_local_teardown(pTHX_ xsh_user_cxt_t *cxt) {
555 15           SvREFCNT_dec(cxt->default_meth);
556 15           cxt->default_meth = NULL;
557              
558 15           return;
559             }
560              
561 15           static void xsh_user_global_teardown(pTHX) {
562 15           xsh_ck_restore(OP_PADANY, <_old_ck_padany);
563 15           xsh_ck_restore(OP_PADSV, <_old_ck_padsv);
564              
565 15           ptable_map_free(lt_op_padxv_map);
566 15           lt_op_padxv_map = NULL;
567              
568             #if XSH_HAS_PERL(5, 17, 6)
569 15           ptable_map_free(lt_op_padrange_map);
570 15           lt_op_padrange_map = NULL;
571             #endif
572              
573             #ifdef USE_ITHREADS
574             MUTEX_DESTROY(<_op_map_mutex);
575             #endif
576              
577 15           return;
578             }
579              
580             /* --- XS ------------------------------------------------------------------ */
581              
582             MODULE = Lexical::Types PACKAGE = Lexical::Types
583              
584             PROTOTYPES: ENABLE
585              
586             BOOT:
587             {
588 15           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 1039 50         if (!SvOK(code))
    0          
    0          
607 0           code = NULL;
608 1039 50         else if (SvROK(code))
609 1039           code = SvRV(code);
610 1039           RETVAL = xsh_hints_tag(code);
611             OUTPUT:
612             RETVAL