File Coverage

lib/Syntax/Keyword/Try.xs
Criterion Covered Total %
statement 199 210 94.7
branch 109 150 72.6
condition n/a
subroutine n/a
pod n/a
total 308 360 85.5


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