File Coverage

hax/forbid_outofblock_ops.c.inc
Criterion Covered Total %
statement 57 60 95.0
branch 36 42 85.7
condition n/a
subroutine n/a
pod n/a
total 93 102 91.1


line stmt bran cond sub pod time code
1             /* vi: set ft=c : */
2              
3             #if !HAVE_PERL_VERSION(5, 16, 0)
4             # define CopLABEL_len_flags(c,len,flags) Perl_fetch_cop_label(aTHX_ (c), len, flags)
5             #endif
6              
7 220           static void walk_ops_find_labels(pTHX_ OP *o, HV *gotolabels)
8             {
9 220 100         switch(o->op_type) {
10             case OP_NEXTSTATE:
11             case OP_DBSTATE:
12             {
13             STRLEN label_len;
14             U32 label_flags;
15 40           const char *label_pv = CopLABEL_len_flags((COP *)o, &label_len, &label_flags);
16 40 100         if(!label_pv)
17             break;
18              
19 3           SV *labelsv = newSVpvn_flags(label_pv, label_len, label_flags);
20 3           SAVEFREESV(labelsv);
21              
22 3           sv_inc(HeVAL(hv_fetch_ent(gotolabels, labelsv, TRUE, 0)));
23 40           break;
24             }
25             }
26              
27 220 100         if(!(o->op_flags & OPf_KIDS))
28             return;
29              
30 86           OP *kid = cUNOPo->op_first;
31 273 100         while(kid) {
32 187           walk_ops_find_labels(aTHX_ kid, gotolabels);
33 187 100         kid = OpSIBLING(kid);
34             }
35             }
36              
37             enum {
38             FORBID_LOOPEX_DEFAULT = (1<<0),
39             };
40              
41 218           static void walk_ops_forbid(pTHX_ OP *o, U32 flags, HV *permittedloops, HV *permittedgotos, const char *blockname)
42             {
43             bool is_loop = FALSE;
44             SV *labelsv = NULL;
45              
46 218           switch(o->op_type) {
47             case OP_NEXTSTATE:
48             case OP_DBSTATE:
49 40           PL_curcop = (COP *)o;
50 40           return;
51              
52             case OP_RETURN:
53             goto forbid;
54              
55             case OP_GOTO:
56             {
57             /* OPf_STACKED means either dynamically computed label or `goto &sub` */
58 3 50         if(o->op_flags & OPf_STACKED)
59             goto forbid;
60              
61 3           SV *target = newSVpv(cPVOPo->op_pv, strlen(cPVOPo->op_pv));
62             #if HAVE_PERL_VERSION(5, 16, 0)
63 3 50         if(cPVOPo->op_private & OPpPV_IS_UTF8)
64 0           SvUTF8_on(target);
65             #endif
66 3           SAVEFREESV(target);
67              
68 3 100         if(hv_fetch_ent(permittedgotos, target, FALSE, 0))
69             break;
70              
71             goto forbid;
72             }
73              
74             case OP_NEXT:
75             case OP_LAST:
76             case OP_REDO:
77             {
78             /* OPf_SPECIAL means this is a default loopex */
79 4 100         if(o->op_flags & OPf_SPECIAL) {
80 2 100         if(flags & FORBID_LOOPEX_DEFAULT)
81             goto forbid;
82              
83             break;
84             }
85             /* OPf_STACKED means it's a dynamically computed label */
86 2 50         if(o->op_flags & OPf_STACKED)
87             goto forbid;
88              
89 2           SV *target = newSVpv(cPVOPo->op_pv, strlen(cPVOPo->op_pv));
90             #if HAVE_PERL_VERSION(5, 16, 0)
91 2 50         if(cPVOPo->op_private & OPpPV_IS_UTF8)
92 0           SvUTF8_on(target);
93             #endif
94 2           SAVEFREESV(target);
95              
96 2 100         if(hv_fetch_ent(permittedloops, target, FALSE, 0))
97             break;
98              
99             goto forbid;
100             }
101              
102             case OP_LEAVELOOP:
103             {
104             STRLEN label_len;
105             U32 label_flags;
106 2           const char *label_pv = CopLABEL_len_flags(PL_curcop, &label_len, &label_flags);
107              
108 2 100         if(label_pv) {
109 1           labelsv = newSVpvn_flags(label_pv, label_len, label_flags);
110 1           SAVEFREESV(labelsv);
111              
112 2           sv_inc(HeVAL(hv_fetch_ent(permittedloops, labelsv, TRUE, 0)));
113             }
114              
115             is_loop = TRUE;
116             break;
117             }
118              
119             forbid:
120 4           croak("Can't \"%s\" out of %s", PL_op_name[o->op_type], blockname);
121              
122             default:
123             break;
124             }
125              
126 174 100         if(!(o->op_flags & OPf_KIDS))
127             return;
128              
129 85           OP *kid = cUNOPo->op_first;
130 266 100         while(kid) {
131 185           walk_ops_forbid(aTHX_ kid, flags, permittedloops, permittedgotos, blockname);
132 181 100         kid = OpSIBLING(kid);
133              
134 181 100         if(is_loop) {
135             /* Now in the body of the loop; we can permit loopex default */
136 181           flags &= ~FORBID_LOOPEX_DEFAULT;
137             }
138             }
139              
140 81 100         if(is_loop && labelsv) {
141 1           HE *he = hv_fetch_ent(permittedloops, labelsv, FALSE, 0);
142 1 50         if(SvIV(HeVAL(he)) > 1)
    50          
143 0           sv_dec(HeVAL(he));
144             else
145 1           hv_delete_ent(permittedloops, labelsv, 0, 0);
146             }
147             }
148              
149             #ifndef forbid_outofblock_ops
150             # define forbid_outofblock_ops(o, blockname) S_forbid_outofblock_ops(aTHX_ o, blockname)
151 33           static void S_forbid_outofblock_ops(pTHX_ OP *o, const char *blockname)
152             {
153 33           ENTER;
154 33           SAVEVPTR(PL_curcop);
155              
156 33           HV *looplabels = newHV();
157 33           SAVEFREESV((SV *)looplabels);
158              
159 33           HV *gotolabels = newHV();
160 33           SAVEFREESV((SV *)gotolabels);
161              
162 33           walk_ops_find_labels(aTHX_ o, gotolabels);
163              
164 33           walk_ops_forbid(aTHX_ o, FORBID_LOOPEX_DEFAULT, looplabels, gotolabels, blockname);
165              
166 29           LEAVE;
167 29           }
168             #endif