File Coverage

hax/forbid_outofblock_ops.c.inc
Criterion Covered Total %
statement 72 77 93.5
branch 39 44 88.6
condition n/a
subroutine n/a
pod n/a
total 111 121 91.7


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 244           static void walk_ops_find_labels(pTHX_ OP *o, HV *gotolabels)
8             {
9 244 100         switch(o->op_type) {
10 44           case OP_NEXTSTATE:
11             case OP_DBSTATE:
12             {
13             STRLEN label_len;
14             U32 label_flags;
15 44           const char *label_pv = CopLABEL_len_flags((COP *)o, &label_len, &label_flags);
16 44 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 3           break;
24             }
25             }
26              
27 244 100         if(!(o->op_flags & OPf_KIDS))
28             return;
29              
30 96           OP *kid = cUNOPo->op_first;
31 304 100         while(kid) {
32 208           walk_ops_find_labels(aTHX_ kid, gotolabels);
33 208 100         kid = OpSIBLING(kid);
34             }
35             }
36              
37             enum {
38             FORBID_LOOPEX_DEFAULT = (1<<0),
39             };
40              
41 242           static OPCODE walk_ops_forbid(pTHX_ OP *o, U32 flags, HV *permittedloops, HV *permittedgotos)
42             {
43             bool is_loop = FALSE;
44             SV *labelsv = NULL;
45              
46 242           switch(o->op_type) {
47 44           case OP_NEXTSTATE:
48             case OP_DBSTATE:
49 44           PL_curcop = (COP *)o;
50 44           return 0;
51              
52 1           case OP_RETURN:
53 1           goto forbid;
54              
55 3           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 0           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 1           goto forbid;
72             }
73              
74 4           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 1           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 0           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 1           goto forbid;
100             }
101              
102 2           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 1           sv_inc(HeVAL(hv_fetch_ent(permittedloops, labelsv, TRUE, 0)));
113             }
114              
115             is_loop = TRUE;
116             break;
117             }
118              
119 4           forbid:
120 4           return o->op_type;
121              
122             default:
123             break;
124             }
125              
126 194 100         if(!(o->op_flags & OPf_KIDS))
127             return 0;
128              
129 95           OP *kid = cUNOPo->op_first;
130 297 100         while(kid) {
131 206           OPCODE ret = walk_ops_forbid(aTHX_ kid, flags, permittedloops, permittedgotos);
132 206 100         if(ret)
133 4           return ret;
134              
135 202 100         kid = OpSIBLING(kid);
136              
137 202 100         if(is_loop) {
138             /* Now in the body of the loop; we can permit loopex default */
139 4           flags &= ~FORBID_LOOPEX_DEFAULT;
140             }
141             }
142              
143 91 100         if(is_loop && labelsv) {
144 1           HE *he = hv_fetch_ent(permittedloops, labelsv, FALSE, 0);
145 1 50         if(SvIV(HeVAL(he)) > 1)
146 0           sv_dec(HeVAL(he));
147             else
148 1           hv_delete_ent(permittedloops, labelsv, 0, 0);
149             }
150              
151             return 0;
152             }
153              
154             #ifndef forbid_outofblock_ops
155             # define forbid_outofblock_ops(o, blockname) S_forbid_outofblock_ops(aTHX_ o, blockname)
156 36           static void S_forbid_outofblock_ops(pTHX_ OP *o, const char *blockname)
157             {
158 36           ENTER;
159 36           SAVEVPTR(PL_curcop);
160              
161 36           HV *looplabels = newHV();
162 36           SAVEFREESV((SV *)looplabels);
163              
164 36           HV *gotolabels = newHV();
165 36           SAVEFREESV((SV *)gotolabels);
166              
167 36           walk_ops_find_labels(aTHX_ o, gotolabels);
168              
169 36           OPCODE forbidden = walk_ops_forbid(aTHX_ o, FORBID_LOOPEX_DEFAULT, looplabels, gotolabels);
170 36 100         if(forbidden)
171 4           croak("Can't \"%s\" out of %s", PL_op_name[forbidden], blockname);
172              
173 32           LEAVE;
174 32           }
175             #endif
176              
177             #ifndef warn_outofblock_ops
178             # define warn_outofblock_ops(o, fmt) S_warn_outofblock_ops(aTHX_ o, fmt)
179             static void S_warn_outofblock_ops(pTHX_ OP *o, const char *fmt)
180             {
181             ENTER;
182             SAVEVPTR(PL_curcop);
183              
184             HV *looplabels = newHV();
185             SAVEFREESV((SV *)looplabels);
186              
187             HV *gotolabels = newHV();
188             SAVEFREESV((SV *)gotolabels);
189              
190             walk_ops_find_labels(aTHX_ o, gotolabels);
191              
192             OPCODE forbidden = walk_ops_forbid(aTHX_ o, FORBID_LOOPEX_DEFAULT, looplabels, gotolabels);
193             if(forbidden)
194             warn(fmt, PL_op_name[forbidden]);
195              
196             LEAVE;
197             }
198             #endif