line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
/* You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
* or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
* |
4
|
|
|
|
|
|
|
* (C) Paul Evans, 2016-2021 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
*/ |
6
|
|
|
|
|
|
|
#include "EXTERN.h" |
7
|
|
|
|
|
|
|
#include "perl.h" |
8
|
|
|
|
|
|
|
#include "XSUB.h" |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#include "XSParseKeyword.h" |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
#include "perl-backcompat.c.inc" |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,32,0) |
15
|
|
|
|
|
|
|
# define HAVE_OP_ISA |
16
|
|
|
|
|
|
|
#endif |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,26,0) |
19
|
|
|
|
|
|
|
# define HAVE_OP_SIBPARENT |
20
|
|
|
|
|
|
|
#endif |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,19,4) |
23
|
|
|
|
|
|
|
typedef SSize_t array_ix_t; |
24
|
|
|
|
|
|
|
#else /* <5.19.4 */ |
25
|
|
|
|
|
|
|
typedef I32 array_ix_t; |
26
|
|
|
|
|
|
|
#endif /* <5.19.4 */ |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#include "perl-additions.c.inc" |
29
|
|
|
|
|
|
|
#include "optree-additions.c.inc" |
30
|
|
|
|
|
|
|
#include "op_sibling_splice.c.inc" |
31
|
|
|
|
|
|
|
#include "newOP_CUSTOM.c.inc" |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
static OP *pp_entertrycatch(pTHX); |
34
|
|
|
|
|
|
|
static OP *pp_catch(pTHX); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
/* |
37
|
|
|
|
|
|
|
* A modified version of pp_return for returning from inside a try block. |
38
|
|
|
|
|
|
|
* To do this, we unwind the context stack to just past the CXt_EVAL and then |
39
|
|
|
|
|
|
|
* chain to the regular OP_RETURN func |
40
|
|
|
|
|
|
|
*/ |
41
|
7
|
|
|
|
|
|
static OP *pp_returnintry(pTHX) |
42
|
|
|
|
|
|
|
{ |
43
|
|
|
|
|
|
|
I32 cxix; |
44
|
|
|
|
|
|
|
|
45
|
23
|
50
|
|
|
|
|
for (cxix = cxstack_ix; cxix; cxix--) { |
46
|
23
|
100
|
|
|
|
|
if(CxTYPE(&cxstack[cxix]) == CXt_SUB) |
47
|
|
|
|
|
|
|
break; |
48
|
|
|
|
|
|
|
|
49
|
17
|
100
|
|
|
|
|
if(CxTYPE(&cxstack[cxix]) == CXt_EVAL && CxTRYBLOCK(&cxstack[cxix])) { |
50
|
|
|
|
|
|
|
/* If this CXt_EVAL frame came from our own ENTERTRYCATCH, then the |
51
|
|
|
|
|
|
|
* retop should point at an OP_CUSTOM and its first grand-child will be |
52
|
|
|
|
|
|
|
* our custom modified ENTERTRY. We can skip over it and continue in |
53
|
|
|
|
|
|
|
* this case. |
54
|
|
|
|
|
|
|
*/ |
55
|
9
|
|
|
|
|
|
OP *retop = cxstack[cxix].blk_eval.retop; |
56
|
|
|
|
|
|
|
OP *leave, *enter; |
57
|
9
|
100
|
|
|
|
|
if(retop->op_type == OP_CUSTOM && retop->op_ppaddr == &pp_catch && |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
58
|
8
|
50
|
|
|
|
|
(leave = cLOGOPx(retop)->op_first) && leave->op_type == OP_LEAVETRY && |
|
|
50
|
|
|
|
|
|
59
|
8
|
50
|
|
|
|
|
(enter = cLOGOPx(leave)->op_first) && enter->op_type == OP_ENTERTRY && |
|
|
50
|
|
|
|
|
|
60
|
8
|
|
|
|
|
|
enter->op_ppaddr == &pp_entertrycatch) { |
61
|
8
|
|
|
|
|
|
continue; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
/* We have to stop at any other kind of CXt_EVAL */ |
64
|
|
|
|
|
|
|
break; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
7
|
50
|
|
|
|
|
if(!cxix) |
68
|
0
|
|
|
|
|
|
croak("Unable to find an CXt_SUB to pop back to"); |
69
|
|
|
|
|
|
|
|
70
|
7
|
|
|
|
|
|
I32 gimme = cxstack[cxix].blk_gimme; |
71
|
|
|
|
|
|
|
SV *retval; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
/* chunks of this code inspired by |
74
|
|
|
|
|
|
|
* ZEFRAM/Scope-Escape-0.005/lib/Scope/Escape.xs |
75
|
|
|
|
|
|
|
*/ |
76
|
7
|
|
|
|
|
|
switch(gimme) { |
77
|
|
|
|
|
|
|
case G_VOID: |
78
|
|
|
|
|
|
|
(void)POPMARK; |
79
|
|
|
|
|
|
|
break; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
case G_SCALAR: { |
82
|
5
|
|
|
|
|
|
dSP; |
83
|
10
|
|
|
|
|
|
dMARK; |
84
|
5
|
50
|
|
|
|
|
retval = (MARK == SP) ? &PL_sv_undef : TOPs; |
85
|
|
|
|
|
|
|
SvREFCNT_inc(retval); |
86
|
5
|
|
|
|
|
|
sv_2mortal(retval); |
87
|
5
|
|
|
|
|
|
break; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
case G_LIST: { |
91
|
1
|
|
|
|
|
|
dSP; |
92
|
2
|
|
|
|
|
|
dMARK; |
93
|
1
|
|
|
|
|
|
SV **retvals = MARK+1; |
94
|
1
|
|
|
|
|
|
array_ix_t retcount = SP-MARK; |
95
|
|
|
|
|
|
|
array_ix_t i; |
96
|
1
|
|
|
|
|
|
AV *retav = newAV(); |
97
|
|
|
|
|
|
|
retval = (SV *)retav; |
98
|
1
|
|
|
|
|
|
sv_2mortal(retval); |
99
|
1
|
|
|
|
|
|
av_fill(retav, retcount-1); |
100
|
1
|
50
|
|
|
|
|
Copy(retvals, AvARRAY(retav), retcount, SV *); |
101
|
4
|
100
|
|
|
|
|
for(i = 0; i < retcount; i++) |
102
|
3
|
|
|
|
|
|
SvREFCNT_inc(retvals[i]); |
103
|
|
|
|
|
|
|
break; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
7
|
|
|
|
|
|
dounwind(cxix); |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
/* Now put the value back */ |
110
|
7
|
|
|
|
|
|
switch(gimme) { |
111
|
|
|
|
|
|
|
case G_VOID: { |
112
|
1
|
|
|
|
|
|
dSP; |
113
|
1
|
50
|
|
|
|
|
PUSHMARK(SP); |
114
|
1
|
|
|
|
|
|
break; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
case G_SCALAR: { |
118
|
5
|
|
|
|
|
|
dSP; |
119
|
5
|
50
|
|
|
|
|
PUSHMARK(SP); |
120
|
5
|
50
|
|
|
|
|
XPUSHs(retval); |
121
|
5
|
|
|
|
|
|
PUTBACK; |
122
|
5
|
|
|
|
|
|
break; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
case G_LIST: { |
126
|
1
|
|
|
|
|
|
dSP; |
127
|
1
|
50
|
|
|
|
|
PUSHMARK(SP); |
128
|
|
|
|
|
|
|
AV *retav = (AV *)retval; |
129
|
1
|
|
|
|
|
|
array_ix_t retcount = av_len(retav) + 1; /* because av_len means top index */ |
130
|
1
|
50
|
|
|
|
|
EXTEND(SP, retcount); |
|
|
50
|
|
|
|
|
|
131
|
1
|
50
|
|
|
|
|
Copy(AvARRAY(retav), SP+1, retcount, SV *); |
132
|
1
|
|
|
|
|
|
SP += retcount; |
133
|
1
|
|
|
|
|
|
PUTBACK; |
134
|
1
|
|
|
|
|
|
break; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
7
|
|
|
|
|
|
return PL_ppaddr[OP_RETURN](aTHX); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
/* |
142
|
|
|
|
|
|
|
* A custom SVOP that takes a CV and arranges for it to be invoked on scope |
143
|
|
|
|
|
|
|
* leave |
144
|
|
|
|
|
|
|
*/ |
145
|
|
|
|
|
|
|
static XOP xop_pushfinally; |
146
|
|
|
|
|
|
|
|
147
|
8
|
|
|
|
|
|
static void invoke_finally(pTHX_ void *arg) |
148
|
|
|
|
|
|
|
{ |
149
|
|
|
|
|
|
|
CV *finally = arg; |
150
|
8
|
|
|
|
|
|
dSP; |
151
|
|
|
|
|
|
|
|
152
|
8
|
50
|
|
|
|
|
PUSHMARK(SP); |
153
|
8
|
|
|
|
|
|
call_sv((SV *)finally, G_DISCARD|G_EVAL|G_KEEPERR); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
SvREFCNT_dec(finally); |
156
|
8
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
8
|
|
|
|
|
|
static OP *pp_pushfinally(pTHX) |
159
|
|
|
|
|
|
|
{ |
160
|
8
|
|
|
|
|
|
CV *finally = (CV *)cSVOP->op_sv; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
/* finally is a closure protosub; we have to clone it into a real sub. |
163
|
|
|
|
|
|
|
* If we do this now then captured lexicals still work even around |
164
|
|
|
|
|
|
|
* Future::AsyncAwait (see RT122796) |
165
|
|
|
|
|
|
|
* */ |
166
|
8
|
|
|
|
|
|
SAVEDESTRUCTOR_X(&invoke_finally, (SV *)cv_clone(finally)); |
167
|
8
|
|
|
|
|
|
return PL_op->op_next; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
#define newLOCALISEOP(gv) MY_newLOCALISEOP(aTHX_ gv) |
171
|
|
|
|
|
|
|
static OP *MY_newLOCALISEOP(pTHX_ GV *gv) |
172
|
|
|
|
|
|
|
{ |
173
|
|
|
|
|
|
|
OP *op = newGVOP(OP_GVSV, 0, gv); |
174
|
|
|
|
|
|
|
op->op_private |= OPpLVAL_INTRO; |
175
|
|
|
|
|
|
|
return op; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
#define newSTATEOP_nowarnings() MY_newSTATEOP_nowarnings(aTHX) |
179
|
2
|
|
|
|
|
|
static OP *MY_newSTATEOP_nowarnings(pTHX) |
180
|
|
|
|
|
|
|
{ |
181
|
2
|
|
|
|
|
|
OP *op = newSTATEOP(0, NULL, NULL); |
182
|
2
|
|
|
|
|
|
STRLEN *warnings = ((COP *)op)->cop_warnings; |
183
|
|
|
|
|
|
|
char *warning_bits; |
184
|
|
|
|
|
|
|
|
185
|
2
|
50
|
|
|
|
|
if(warnings == pWARN_NONE) |
186
|
|
|
|
|
|
|
return op; |
187
|
|
|
|
|
|
|
|
188
|
2
|
50
|
|
|
|
|
if(warnings == pWARN_STD) |
189
|
|
|
|
|
|
|
/* TODO: understand what STD vs ALL means */ |
190
|
|
|
|
|
|
|
warning_bits = WARN_ALLstring; |
191
|
2
|
50
|
|
|
|
|
else if(warnings == pWARN_ALL) |
192
|
|
|
|
|
|
|
warning_bits = WARN_ALLstring; |
193
|
|
|
|
|
|
|
else |
194
|
0
|
|
|
|
|
|
warning_bits = (char *)(warnings + 1); |
195
|
|
|
|
|
|
|
|
196
|
2
|
|
|
|
|
|
warnings = Perl_new_warnings_bitfield(aTHX_ warnings, warning_bits, WARNsize); |
197
|
2
|
|
|
|
|
|
((COP *)op)->cop_warnings = warnings; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
warning_bits = (char *)(warnings + 1); |
200
|
2
|
|
|
|
|
|
warning_bits[(2*WARN_EXITING) / 8] &= ~(1 << (2*WARN_EXITING % 8)); |
201
|
|
|
|
|
|
|
|
202
|
2
|
|
|
|
|
|
return op; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
14
|
|
|
|
|
|
static void rethread_op(OP *op, OP *old, OP *new) |
206
|
|
|
|
|
|
|
{ |
207
|
14
|
100
|
|
|
|
|
if(op->op_next == old) |
208
|
1
|
|
|
|
|
|
op->op_next = new; |
209
|
|
|
|
|
|
|
|
210
|
14
|
50
|
|
|
|
|
switch(OP_CLASS(op)) { |
211
|
|
|
|
|
|
|
case OA_LOGOP: |
212
|
1
|
50
|
|
|
|
|
if(cLOGOPx(op)->op_other == old) |
213
|
1
|
|
|
|
|
|
cLOGOPx(op)->op_other = new; |
214
|
|
|
|
|
|
|
break; |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
case OA_LISTOP: |
217
|
2
|
50
|
|
|
|
|
if(cLISTOPx(op)->op_last == old) |
218
|
0
|
|
|
|
|
|
cLISTOPx(op)->op_last = new; |
219
|
|
|
|
|
|
|
break; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
14
|
100
|
|
|
|
|
if(op->op_flags & OPf_KIDS) { |
223
|
|
|
|
|
|
|
OP *kid; |
224
|
18
|
100
|
|
|
|
|
for(kid = cUNOPx(op)->op_first; kid; kid = OpSIBLING(kid)) |
|
|
100
|
|
|
|
|
|
225
|
12
|
|
|
|
|
|
rethread_op(kid, old, new); |
226
|
|
|
|
|
|
|
} |
227
|
14
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
#define walk_optree_try_in_eval(op_ptr, root) MY_walk_optree_try_in_eval(aTHX_ op_ptr, root) |
230
|
|
|
|
|
|
|
static void MY_walk_optree_try_in_eval(pTHX_ OP **op_ptr, OP *root); |
231
|
260
|
|
|
|
|
|
static void MY_walk_optree_try_in_eval(pTHX_ OP **op_ptr, OP *root) |
232
|
|
|
|
|
|
|
{ |
233
|
260
|
|
|
|
|
|
OP *op = *op_ptr; |
234
|
|
|
|
|
|
|
|
235
|
260
|
|
|
|
|
|
switch(op->op_type) { |
236
|
|
|
|
|
|
|
/* Fix 'return' to unwind the CXt_EVAL block that implements try{} first */ |
237
|
|
|
|
|
|
|
case OP_RETURN: |
238
|
7
|
|
|
|
|
|
op->op_ppaddr = &pp_returnintry; |
239
|
7
|
|
|
|
|
|
break; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
/* wrap no warnings 'exiting' around loop controls */ |
242
|
|
|
|
|
|
|
case OP_NEXT: |
243
|
|
|
|
|
|
|
case OP_LAST: |
244
|
|
|
|
|
|
|
case OP_REDO: |
245
|
|
|
|
|
|
|
{ |
246
|
|
|
|
|
|
|
#ifdef HAVE_OP_SIBPARENT |
247
|
2
|
100
|
|
|
|
|
OP *parent = OpHAS_SIBLING(op) ? NULL : op->op_sibparent; |
248
|
|
|
|
|
|
|
#endif |
249
|
|
|
|
|
|
|
|
250
|
2
|
|
|
|
|
|
OP *stateop = newSTATEOP_nowarnings(); |
251
|
|
|
|
|
|
|
|
252
|
2
|
|
|
|
|
|
OP *scope = newLISTOP(OP_SCOPE, 0, |
253
|
|
|
|
|
|
|
stateop, op); |
254
|
|
|
|
|
|
|
#ifdef HAVE_OP_SIBPARENT |
255
|
2
|
100
|
|
|
|
|
if(parent) |
256
|
1
|
|
|
|
|
|
OpLASTSIB_set(scope, parent); |
257
|
|
|
|
|
|
|
else |
258
|
1
|
|
|
|
|
|
OpLASTSIB_set(scope, NULL); |
259
|
|
|
|
|
|
|
#else |
260
|
|
|
|
|
|
|
op->op_sibling = NULL; |
261
|
|
|
|
|
|
|
#endif |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
/* Rethread */ |
264
|
2
|
|
|
|
|
|
scope->op_next = stateop; |
265
|
2
|
|
|
|
|
|
stateop->op_next = op; |
266
|
|
|
|
|
|
|
|
267
|
2
|
|
|
|
|
|
*op_ptr = scope; |
268
|
|
|
|
|
|
|
} |
269
|
2
|
|
|
|
|
|
break; |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
/* Don't enter inside nested eval{} blocks */ |
272
|
|
|
|
|
|
|
case OP_LEAVETRY: |
273
|
|
|
|
|
|
|
return; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
258
|
100
|
|
|
|
|
if(op->op_flags & OPf_KIDS) { |
277
|
|
|
|
|
|
|
OP *kid, *next, *prev = NULL; |
278
|
334
|
100
|
|
|
|
|
for(kid = cUNOPx(op)->op_first; kid; kid = next) { |
279
|
222
|
|
|
|
|
|
OP *newkid = kid; |
280
|
222
|
100
|
|
|
|
|
next = OpSIBLING(kid); |
281
|
|
|
|
|
|
|
|
282
|
222
|
|
|
|
|
|
walk_optree_try_in_eval(&newkid, root); |
283
|
|
|
|
|
|
|
|
284
|
222
|
100
|
|
|
|
|
if(newkid != kid) { |
285
|
2
|
|
|
|
|
|
rethread_op(root, kid, newkid); |
286
|
|
|
|
|
|
|
|
287
|
2
|
50
|
|
|
|
|
if(prev) { |
288
|
2
|
|
|
|
|
|
OpMORESIB_set(prev, newkid); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
else |
291
|
0
|
|
|
|
|
|
cUNOPx(op)->op_first = newkid; |
292
|
|
|
|
|
|
|
|
293
|
2
|
100
|
|
|
|
|
if(next) |
294
|
222
|
|
|
|
|
|
OpMORESIB_set(newkid, next); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
prev = kid; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
43
|
|
|
|
|
|
static OP *pp_entertrycatch(pTHX) |
303
|
|
|
|
|
|
|
{ |
304
|
|
|
|
|
|
|
/* Localise the errgv */ |
305
|
43
|
|
|
|
|
|
save_scalar(PL_errgv); |
306
|
|
|
|
|
|
|
|
307
|
43
|
|
|
|
|
|
return PL_ppaddr[OP_ENTERTRY](aTHX); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
static XOP xop_catch; |
311
|
|
|
|
|
|
|
|
312
|
33
|
|
|
|
|
|
static OP *pp_catch(pTHX) |
313
|
|
|
|
|
|
|
{ |
314
|
|
|
|
|
|
|
/* If an error didn't happen, then ERRSV will be both not true and not a |
315
|
|
|
|
|
|
|
* reference. If it's a reference, then an error definitely happened |
316
|
|
|
|
|
|
|
*/ |
317
|
33
|
50
|
|
|
|
|
if(SvROK(ERRSV) || SvTRUE(ERRSV)) |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
318
|
22
|
|
|
|
|
|
return cLOGOP->op_other; |
319
|
|
|
|
|
|
|
else |
320
|
11
|
|
|
|
|
|
return cLOGOP->op_next; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
#define newENTERTRYCATCHOP(flags, try, catch) MY_newENTERTRYCATCHOP(aTHX_ flags, try, catch) |
324
|
76
|
|
|
|
|
|
static OP *MY_newENTERTRYCATCHOP(pTHX_ U32 flags, OP *try, OP *catch) |
325
|
|
|
|
|
|
|
{ |
326
|
|
|
|
|
|
|
OP *enter, *entertry, *ret; |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
/* Walk the block for OP_RETURN ops, so we can apply a hack to them to |
329
|
|
|
|
|
|
|
* make |
330
|
|
|
|
|
|
|
* try { return } |
331
|
|
|
|
|
|
|
* return from the containing sub, not just the eval block |
332
|
|
|
|
|
|
|
*/ |
333
|
38
|
|
|
|
|
|
walk_optree_try_in_eval(&try, try); |
334
|
|
|
|
|
|
|
|
335
|
38
|
|
|
|
|
|
enter = newUNOP(OP_ENTERTRY, 0, try); |
336
|
|
|
|
|
|
|
/* despite calling newUNOP(OP_ENTERTRY,...) the returned root node is the |
337
|
|
|
|
|
|
|
* OP_LEAVETRY, whose first child is the ENTERTRY we wanted |
338
|
|
|
|
|
|
|
*/ |
339
|
38
|
|
|
|
|
|
entertry = ((UNOP *)enter)->op_first; |
340
|
38
|
|
|
|
|
|
entertry->op_ppaddr = &pp_entertrycatch; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
/* If we call newLOGOP_CUSTOM it will op_contextualize the enter block into |
343
|
|
|
|
|
|
|
* G_SCALAR. This is not what we want |
344
|
|
|
|
|
|
|
*/ |
345
|
|
|
|
|
|
|
{ |
346
|
|
|
|
|
|
|
LOGOP *logop; |
347
|
|
|
|
|
|
|
|
348
|
38
|
|
|
|
|
|
OP *first = enter, *other = newLISTOP(OP_SCOPE, 0, catch, NULL); |
349
|
|
|
|
|
|
|
|
350
|
38
|
|
|
|
|
|
NewOp(1101, logop, 1, LOGOP); |
351
|
|
|
|
|
|
|
|
352
|
38
|
|
|
|
|
|
logop->op_type = OP_CUSTOM; |
353
|
38
|
|
|
|
|
|
logop->op_ppaddr = &pp_catch; |
354
|
38
|
|
|
|
|
|
logop->op_first = first; |
355
|
38
|
|
|
|
|
|
logop->op_flags = OPf_KIDS; |
356
|
38
|
50
|
|
|
|
|
logop->op_other = LINKLIST(other); |
357
|
|
|
|
|
|
|
|
358
|
38
|
50
|
|
|
|
|
logop->op_next = LINKLIST(first); |
359
|
38
|
|
|
|
|
|
enter->op_next = (OP *)logop; |
360
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5, 22, 0) |
361
|
38
|
|
|
|
|
|
op_sibling_splice((OP *)logop, first, 0, other); |
362
|
|
|
|
|
|
|
#else |
363
|
|
|
|
|
|
|
first->op_sibling = other; |
364
|
|
|
|
|
|
|
#endif |
365
|
|
|
|
|
|
|
|
366
|
38
|
|
|
|
|
|
ret = newUNOP(OP_NULL, 0, (OP *)logop); |
367
|
38
|
|
|
|
|
|
other->op_next = ret; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
38
|
|
|
|
|
|
return ret; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
#ifndef HAVE_OP_ISA |
374
|
|
|
|
|
|
|
static XOP xop_isa; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
/* Totally stolen from perl 5.32.0's pp.c */ |
377
|
|
|
|
|
|
|
#define sv_isa_sv(sv, namesv) S_sv_isa_sv(aTHX_ sv, namesv) |
378
|
5
|
|
|
|
|
|
static bool S_sv_isa_sv(pTHX_ SV *sv, SV *namesv) |
379
|
|
|
|
|
|
|
{ |
380
|
5
|
100
|
|
|
|
|
if(!SvROK(sv) || !SvOBJECT(SvRV(sv))) |
|
|
50
|
|
|
|
|
|
381
|
|
|
|
|
|
|
return FALSE; |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
/* TODO: ->isa invocation */ |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,16,0) |
386
|
2
|
|
|
|
|
|
return sv_derived_from_sv(sv, namesv, 0); |
387
|
|
|
|
|
|
|
#else |
388
|
|
|
|
|
|
|
return sv_derived_from(sv, SvPV_nolen(namesv)); |
389
|
|
|
|
|
|
|
#endif |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
5
|
|
|
|
|
|
static OP *pp_isa(pTHX) |
393
|
|
|
|
|
|
|
{ |
394
|
5
|
|
|
|
|
|
dSP; |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
SV *left, *right; |
397
|
|
|
|
|
|
|
|
398
|
5
|
|
|
|
|
|
right = POPs; |
399
|
5
|
|
|
|
|
|
left = TOPs; |
400
|
|
|
|
|
|
|
|
401
|
5
|
100
|
|
|
|
|
SETs(boolSV(sv_isa_sv(left, right))); |
402
|
5
|
|
|
|
|
|
RETURN; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
#endif |
405
|
|
|
|
|
|
|
|
406
|
47
|
|
|
|
|
|
static int build_try(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata) |
407
|
|
|
|
|
|
|
{ |
408
|
|
|
|
|
|
|
U32 argi = 0; |
409
|
|
|
|
|
|
|
|
410
|
47
|
|
|
|
|
|
OP *try = args[argi++]->op; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
OP *ret = NULL; |
413
|
47
|
|
|
|
|
|
HV *hints = GvHV(PL_hintgv); |
414
|
|
|
|
|
|
|
|
415
|
47
|
50
|
|
|
|
|
bool require_catch = hints && hv_fetchs(hints, "Syntax::Keyword::Try/require_catch", 0); |
|
|
50
|
|
|
|
|
|
416
|
47
|
50
|
|
|
|
|
bool require_var = hints && hv_fetchs(hints, "Syntax::Keyword::Try/require_var", 0); |
|
|
100
|
|
|
|
|
|
417
|
|
|
|
|
|
|
|
418
|
47
|
|
|
|
|
|
U32 ncatches = args[argi++]->i; |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
AV *condcatch = NULL; |
421
|
|
|
|
|
|
|
OP *catch = NULL; |
422
|
87
|
100
|
|
|
|
|
while(ncatches--) { |
423
|
41
|
|
|
|
|
|
bool has_catchvar = args[argi++]->i; |
424
|
41
|
100
|
|
|
|
|
PADOFFSET catchvar = has_catchvar ? args[argi++]->padix : 0; |
425
|
41
|
100
|
|
|
|
|
int catchtype = has_catchvar ? args[argi++]->i : -1; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
bool warned = FALSE; |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
OP *condop = NULL; |
430
|
|
|
|
|
|
|
|
431
|
41
|
|
|
|
|
|
switch(catchtype) { |
432
|
|
|
|
|
|
|
case -1: /* no type */ |
433
|
|
|
|
|
|
|
break; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
case 0: /* isa */ |
436
|
|
|
|
|
|
|
{ |
437
|
2
|
|
|
|
|
|
OP *type = args[argi++]->op; |
438
|
|
|
|
|
|
|
#ifdef HAVE_OP_ISA |
439
|
|
|
|
|
|
|
condop = newBINOP(OP_ISA, 0, |
440
|
|
|
|
|
|
|
newPADxVOP(OP_PADSV, catchvar, 0, 0), type); |
441
|
|
|
|
|
|
|
#else |
442
|
|
|
|
|
|
|
/* Allow a bareword on RHS of `isa` */ |
443
|
2
|
50
|
|
|
|
|
if(type->op_type == OP_CONST) |
444
|
2
|
|
|
|
|
|
type->op_private &= ~(OPpCONST_BARE|OPpCONST_STRICT); |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
condop = newBINOP_CUSTOM(&pp_isa, 0, |
447
|
|
|
|
|
|
|
newPADxVOP(OP_PADSV, catchvar, 0, 0), type); |
448
|
|
|
|
|
|
|
#endif |
449
|
2
|
|
|
|
|
|
break; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
case 1: /* =~ */ |
453
|
|
|
|
|
|
|
{ |
454
|
1
|
|
|
|
|
|
OP *regexp = args[argi++]->op; |
455
|
|
|
|
|
|
|
|
456
|
1
|
50
|
|
|
|
|
if(regexp->op_type != OP_MATCH || cPMOPx(regexp)->op_first) |
|
|
50
|
|
|
|
|
|
457
|
0
|
|
|
|
|
|
croak("Expected a regexp match"); |
458
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,22,0) |
459
|
|
|
|
|
|
|
/* Perl 5.22+ uses op_targ on OP_MATCH directly */ |
460
|
1
|
|
|
|
|
|
regexp->op_targ = catchvar; |
461
|
|
|
|
|
|
|
#else |
462
|
|
|
|
|
|
|
/* Older perls need a stacked OP_PADSV op */ |
463
|
|
|
|
|
|
|
cPMOPx(regexp)->op_first = newPADxVOP(OP_PADSV, catchvar, 0, 0); |
464
|
|
|
|
|
|
|
regexp->op_flags |= OPf_KIDS|OPf_STACKED; |
465
|
|
|
|
|
|
|
#endif |
466
|
|
|
|
|
|
|
condop = regexp; |
467
|
1
|
|
|
|
|
|
break; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
default: |
471
|
0
|
|
|
|
|
|
croak("TODO\n"); |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
#ifdef WARN_EXPERIMENTAL |
475
|
41
|
100
|
|
|
|
|
if(condop && !warned && |
|
|
50
|
|
|
|
|
|
476
|
3
|
50
|
|
|
|
|
(!hints || !hv_fetchs(hints, "Syntax::Keyword::Try/experimental(typed)", 0))) { |
477
|
|
|
|
|
|
|
warned = true; |
478
|
0
|
|
|
|
|
|
Perl_ck_warner(aTHX_ packWARN(WARN_EXPERIMENTAL), |
479
|
|
|
|
|
|
|
"typed catch syntax is experimental and may be changed or removed without notice"); |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
#endif |
482
|
|
|
|
|
|
|
|
483
|
41
|
|
|
|
|
|
OP *body = args[argi++]->op; |
484
|
|
|
|
|
|
|
|
485
|
41
|
100
|
|
|
|
|
if(require_var && !has_catchvar) |
486
|
1
|
|
|
|
|
|
croak("Expected (VAR) for catch"); |
487
|
|
|
|
|
|
|
|
488
|
40
|
50
|
|
|
|
|
if(catch) |
489
|
0
|
|
|
|
|
|
croak("Already have a default catch {} block"); |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
OP *assignop = NULL; |
492
|
40
|
100
|
|
|
|
|
if(catchvar) { |
493
|
|
|
|
|
|
|
/* my $var = $@ */ |
494
|
37
|
|
|
|
|
|
assignop = newBINOP(OP_SASSIGN, 0, |
495
|
|
|
|
|
|
|
newGVOP(OP_GVSV, 0, PL_errgv), newPADxVOP(OP_PADSV, catchvar, OPf_MOD, OPpLVAL_INTRO)); |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
40
|
100
|
|
|
|
|
if(condop) { |
499
|
3
|
100
|
|
|
|
|
if(!condcatch) |
500
|
2
|
|
|
|
|
|
condcatch = newAV(); |
501
|
|
|
|
|
|
|
|
502
|
3
|
|
|
|
|
|
av_push(condcatch, (SV *)op_append_elem(OP_LINESEQ, assignop, condop)); |
503
|
3
|
|
|
|
|
|
av_push(condcatch, (SV *)body); |
504
|
|
|
|
|
|
|
/* catch remains NULL for now */ |
505
|
|
|
|
|
|
|
} |
506
|
37
|
100
|
|
|
|
|
else if(assignop) { |
507
|
40
|
|
|
|
|
|
catch = op_prepend_elem(OP_LINESEQ, |
508
|
|
|
|
|
|
|
assignop, body); |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
else |
511
|
|
|
|
|
|
|
catch = body; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
46
|
100
|
|
|
|
|
if(condcatch) { |
515
|
|
|
|
|
|
|
I32 i; |
516
|
|
|
|
|
|
|
|
517
|
2
|
100
|
|
|
|
|
if(!catch) |
518
|
|
|
|
|
|
|
/* A default fallthrough */ |
519
|
|
|
|
|
|
|
/* die $@ */ |
520
|
1
|
|
|
|
|
|
catch = newLISTOP(OP_DIE, 0, |
521
|
|
|
|
|
|
|
newOP(OP_PUSHMARK, 0), newGVOP(OP_GVSV, 0, PL_errgv)); |
522
|
|
|
|
|
|
|
|
523
|
5
|
50
|
|
|
|
|
for(i = AvFILL(condcatch)-1; i >= 0; i -= 2) { |
|
|
100
|
|
|
|
|
|
524
|
3
|
|
|
|
|
|
OP *body = (OP *)av_pop(condcatch), |
525
|
3
|
|
|
|
|
|
*condop = (OP *)av_pop(condcatch); |
526
|
|
|
|
|
|
|
|
527
|
3
|
|
|
|
|
|
catch = newCONDOP(0, condop, op_scope(body), catch); |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
SvREFCNT_dec(condcatch); |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
46
|
50
|
|
|
|
|
if(require_catch && !catch) |
534
|
0
|
|
|
|
|
|
croak("Expected a catch {} block"); |
535
|
|
|
|
|
|
|
|
536
|
46
|
50
|
|
|
|
|
bool no_finally = hints && hv_fetchs(hints, "Syntax::Keyword::Try/no_finally", 0); |
|
|
100
|
|
|
|
|
|
537
|
|
|
|
|
|
|
|
538
|
46
|
|
|
|
|
|
U32 has_finally = args[argi++]->i; |
539
|
46
|
100
|
|
|
|
|
CV *finally = has_finally ? args[argi++]->cv : NULL; |
540
|
|
|
|
|
|
|
|
541
|
46
|
100
|
|
|
|
|
if(no_finally && finally) |
542
|
1
|
|
|
|
|
|
croak("finally {} is not permitted here"); |
543
|
|
|
|
|
|
|
|
544
|
45
|
50
|
|
|
|
|
if(!catch && !finally) { |
545
|
0
|
|
|
|
|
|
op_free(try); |
546
|
0
|
0
|
|
|
|
|
croak(no_finally |
547
|
|
|
|
|
|
|
? "Expected try {} to be followed by catch {}" |
548
|
|
|
|
|
|
|
: "Expected try {} to be followed by either catch {} or finally {}"); |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
ret = try; |
552
|
|
|
|
|
|
|
|
553
|
45
|
100
|
|
|
|
|
if(catch) { |
554
|
38
|
|
|
|
|
|
ret = newENTERTRYCATCHOP(0, try, catch); |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
/* If there's a finally, make |
558
|
|
|
|
|
|
|
* $RET = OP_PUSHFINALLY($FINALLY); $RET |
559
|
|
|
|
|
|
|
*/ |
560
|
45
|
100
|
|
|
|
|
if(finally) { |
561
|
10
|
|
|
|
|
|
ret = op_prepend_elem(OP_LINESEQ, |
562
|
|
|
|
|
|
|
newSVOP_CUSTOM(&pp_pushfinally, 0, (SV *)finally), |
563
|
|
|
|
|
|
|
ret); |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
45
|
|
|
|
|
|
ret = op_append_list(OP_LEAVE, |
567
|
|
|
|
|
|
|
newOP(OP_ENTER, 0), |
568
|
|
|
|
|
|
|
ret); |
569
|
|
|
|
|
|
|
|
570
|
45
|
|
|
|
|
|
*out = ret; |
571
|
45
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
static struct XSParseKeywordHooks hooks_try = { |
575
|
|
|
|
|
|
|
.permit_hintkey = "Syntax::Keyword::Try/try", |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
.pieces = (const struct XSParseKeywordPieceType []){ |
578
|
|
|
|
|
|
|
XPK_BLOCK, |
579
|
|
|
|
|
|
|
XPK_REPEATED( |
580
|
|
|
|
|
|
|
XPK_LITERAL("catch"), |
581
|
|
|
|
|
|
|
XPK_PREFIXED_BLOCK( |
582
|
|
|
|
|
|
|
/* optionally ($var), ($var isa Type) or ($var =~ m/.../) */ |
583
|
|
|
|
|
|
|
XPK_PARENSCOPE_OPT( |
584
|
|
|
|
|
|
|
XPK_LEXVAR_MY(XPK_LEXVAR_SCALAR), |
585
|
|
|
|
|
|
|
XPK_CHOICE( |
586
|
|
|
|
|
|
|
XPK_SEQUENCE(XPK_LITERAL("isa"), XPK_TERMEXPR), |
587
|
|
|
|
|
|
|
XPK_SEQUENCE(XPK_LITERAL("=~"), XPK_TERMEXPR) |
588
|
|
|
|
|
|
|
) |
589
|
|
|
|
|
|
|
) |
590
|
|
|
|
|
|
|
) |
591
|
|
|
|
|
|
|
), |
592
|
|
|
|
|
|
|
XPK_OPTIONAL( |
593
|
|
|
|
|
|
|
XPK_LITERAL("finally"), XPK_ANONSUB |
594
|
|
|
|
|
|
|
), |
595
|
|
|
|
|
|
|
{0}, |
596
|
|
|
|
|
|
|
}, |
597
|
|
|
|
|
|
|
.build = &build_try, |
598
|
|
|
|
|
|
|
}; |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
MODULE = Syntax::Keyword::Try PACKAGE = Syntax::Keyword::Try |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
BOOT: |
603
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_catch, xop_name, "catch"); |
604
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_catch, xop_desc, |
605
|
|
|
|
|
|
|
"optionally invoke the catch block if required"); |
606
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_catch, xop_class, OA_LOGOP); |
607
|
15
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_catch, &xop_catch); |
608
|
|
|
|
|
|
|
|
609
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_pushfinally, xop_name, "pushfinally"); |
610
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_pushfinally, xop_desc, |
611
|
|
|
|
|
|
|
"arrange for a CV to be invoked at scope exit"); |
612
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_pushfinally, xop_class, OA_SVOP); |
613
|
15
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_pushfinally, &xop_pushfinally); |
614
|
|
|
|
|
|
|
#ifndef HAVE_OP_ISA |
615
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_isa, xop_name, "isa"); |
616
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_isa, xop_desc, |
617
|
|
|
|
|
|
|
"check if a value is an object of the given class"); |
618
|
15
|
|
|
|
|
|
XopENTRY_set(&xop_isa, xop_class, OA_BINOP); |
619
|
15
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_isa, &xop_isa); |
620
|
|
|
|
|
|
|
#endif |
621
|
|
|
|
|
|
|
|
622
|
15
|
|
|
|
|
|
boot_xs_parse_keyword(0.06); |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
register_xs_parse_keyword("try", &hooks_try, NULL); |