File Coverage

lib/Future/AsyncAwait.xs
Criterion Covered Total %
statement 683 909 75.1
branch 394 810 48.6
condition n/a
subroutine n/a
pod n/a
total 1077 1719 62.6


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-2022 -- leonerd@leonerd.org.uk
5             */
6             #define PERL_NO_GET_CONTEXT
7              
8             #include "EXTERN.h"
9             #include "perl.h"
10             #include "XSUB.h"
11              
12             #include "AsyncAwait.h"
13              
14             #ifdef HAVE_DMD_HELPER
15             # define WANT_DMD_API_044
16             # include "DMD_helper.h"
17             #endif
18              
19             #include "XSParseKeyword.h"
20             #include "XSParseSublike.h"
21              
22             #include "perl-backcompat.c.inc"
23              
24             #if !HAVE_PERL_VERSION(5, 24, 0)
25             /* On perls before 5.24 we have to do some extra work to save the itervar
26             * from being thrown away */
27             # define HAVE_ITERVAR
28             #endif
29              
30             #if HAVE_PERL_VERSION(5, 24, 0)
31             /* For unknown reasons, doing this on perls 5.20 or 5.22 massively breaks
32             * everything.
33             * https://rt.cpan.org/Ticket/Display.html?id=129202#txn-1843918
34             */
35             # define HAVE_FUTURE_CHAIN_CANCEL
36             #endif
37              
38             #if HAVE_PERL_VERSION(5, 26, 0)
39             # define HAVE_OP_ARGCHECK
40             #endif
41              
42             #if HAVE_PERL_VERSION(5, 33, 7)
43             /* perl 5.33.7 added CXp_TRY and the CxTRY macro for true try/catch semantics */
44             # define HAVE_CX_TRY
45             #endif
46              
47             #ifdef SAVEt_CLEARPADRANGE
48             # include "save_clearpadrange.c.inc"
49             #endif
50              
51             #if !HAVE_PERL_VERSION(5, 24, 0)
52             # include "cx_pushblock.c.inc"
53             # include "cx_pusheval.c.inc"
54             #endif
55              
56             #include "perl-additions.c.inc"
57             #include "newOP_CUSTOM.c.inc"
58             #include "cv_copy_flags.c.inc"
59              
60             /* Currently no version of perl makes this visible, so we always want it. Maybe
61             * one day in the future we can make it version-dependent
62             */
63              
64             static void panic(char *fmt, ...);
65              
66             #ifndef NOT_REACHED
67             # define NOT_REACHED STMT_START { panic("Unreachable\n"); } STMT_END
68             #endif
69             #include "docatch.c.inc"
70              
71             typedef struct SuspendedFrame SuspendedFrame;
72             struct SuspendedFrame {
73             SuspendedFrame *next;
74             U8 type;
75             U8 gimme;
76              
77             U32 stacklen;
78             SV **stack;
79              
80             U32 marklen;
81             I32 *marks;
82              
83             COP *oldcop;
84              
85             /* items from the save stack */
86             U32 savedlen;
87             struct Saved {
88             U8 type;
89             union {
90             struct {
91             PADOFFSET padix;
92             U32 count;
93             } clearpad; /* for SAVEt_CLEARSV and SAVEt_CLEARPADRANGE */
94             struct {
95             void (*func)(pTHX_ void *data);
96             void *data;
97             } dx; /* for SAVEt_DESTRUCTOR_X */
98             GV *gv; /* for SAVEt_SV + cur.sv, saved.sv */
99             int *iptr; /* for SAVEt_INT... */
100             STRLEN *lenptr; /* for SAVEt_STRLEN + cur.len, saved.len */
101             PADOFFSET padix; /* for SAVEt_PADSV_AND_MORTALIZE, SAVEt_SPTR */
102             SV *sv; /* for SAVEt_ITEM */
103             struct {
104             SV *sv;
105             U32 mask, set;
106             } svflags; /* for SAVEt_SET_SVFLAGS */
107             } u;
108              
109             union {
110             SV *sv; /* for SAVEt_SV, SAVEt_FREESV, SAVEt_ITEM */
111             void *ptr; /* for SAVEt_COMPPAD, */
112             int i; /* for SAVEt_INT... */
113             STRLEN len; /* for SAVEt_STRLEN */
114             } cur, /* the current value that *thing that we should restore to */
115             saved; /* the saved value we should push to the savestack on restore */
116             } *saved;
117              
118             union {
119             struct {
120             OP *retop;
121             } eval;
122             struct block_loop loop;
123             } el;
124              
125             /* for debugging purposes */
126             SV *loop_list_first_item;
127              
128             #ifdef HAVE_ITERVAR
129             SV *itervar;
130             #endif
131             U32 scopes;
132              
133             U32 mortallen;
134             SV **mortals;
135             };
136              
137             typedef struct {
138             SV *awaiting_future; /* the Future that 'await' is currently waiting for */
139             SV *returning_future; /* the Future that its contining CV will eventually return */
140             COP *curcop; /* value of PL_curcop at suspend time */
141             SuspendedFrame *frames;
142              
143             U32 padlen;
144             SV **padslots;
145              
146             PMOP *curpm; /* value of PL_curpm at suspend time */
147             AV *defav; /* value of GvAV(PL_defgv) at suspend time */
148              
149             HV *modhookdata;
150             } SuspendedState;
151              
152             #ifdef DEBUG
153             # define TRACEPRINT S_traceprint
154             static void S_traceprint(char *fmt, ...)
155             {
156             /* TODO: make conditional */
157             va_list args;
158             va_start(args, fmt);
159             vfprintf(stderr, fmt, args);
160             va_end(args);
161             }
162             #else
163             # define TRACEPRINT(...)
164             #endif
165              
166 0           static void vpanic(char *fmt, va_list args)
167             {
168 0           fprintf(stderr, "Future::AsyncAwait panic: ");
169 0           vfprintf(stderr, fmt, args);
170 0           raise(SIGABRT);
171 0           }
172              
173 0           static void panic(char *fmt, ...)
174             {
175             va_list args;
176 0           va_start(args, fmt);
177 0           vpanic(fmt, args);
178 0           }
179              
180             /*
181             * Hook mechanism
182             */
183              
184             struct HookRegistration
185             {
186             const struct AsyncAwaitHookFuncs *funcs;
187             void *data;
188             };
189              
190             struct HookRegistrations
191             {
192             struct HookRegistration *arr;
193             size_t count, size;
194             };
195              
196 252           static struct HookRegistrations *S_registrations(pTHX_ bool add)
197             {
198 252           SV *regsv = *hv_fetchs(PL_modglobal, "Future::AsyncAwait/registrations", GV_ADD);
199 252 50         if(!SvOK(regsv)) {
    50          
    50          
200 252 50         if(!add)
201             return NULL;
202              
203             struct HookRegistrations *registrations;
204 0           Newx(registrations, 1, struct HookRegistrations);
205              
206 0           registrations->count = 0;
207 0           registrations->size = 4;
208 0           Newx(registrations->arr, registrations->size, struct HookRegistration);
209              
210 0           sv_setuv(regsv, PTR2UV(registrations));
211             }
212              
213 0 0         return INT2PTR(struct HookRegistrations *, SvUV(regsv));
214             }
215             #define registrations(add) S_registrations(aTHX_ add)
216              
217 0           static void register_faa_hook(pTHX_ const struct AsyncAwaitHookFuncs *hookfuncs, void *hookdata)
218             {
219             /* Currently no flags are recognised; complain if the caller requested any */
220 0 0         if(hookfuncs->flags)
221 0           croak("Unrecognised hookfuncs->flags value %08x", hookfuncs->flags);
222              
223 0           struct HookRegistrations *regs = registrations(TRUE);
224              
225 0 0         if(regs->count == regs->size) {
226 0           regs->size *= 2;
227 0 0         Renew(regs->arr, regs->size, struct HookRegistration);
228             }
229              
230 0           regs->arr[regs->count].funcs = hookfuncs;
231 0           regs->arr[regs->count].data = hookdata;
232 0           regs->count++;
233 0           }
234              
235             #define RUN_HOOKS_FWD(func, ...) \
236             { \
237             int _hooki = 0; \
238             while(_hooki < regs->count) { \
239             struct HookRegistration *reg = regs->arr + _hooki; \
240             if(reg->funcs->func) \
241             (*reg->funcs->func)(aTHX_ __VA_ARGS__, reg->data); \
242             _hooki++; \
243             } \
244             }
245              
246             #define RUN_HOOKS_REV(func, ...) \
247             { \
248             int _hooki = regs->count; \
249             while(_hooki > 0) { \
250             _hooki--; \
251             struct HookRegistration *reg = regs->arr + _hooki; \
252             if(reg->funcs->func) \
253             (*reg->funcs->func)(aTHX_ __VA_ARGS__, reg->data); \
254             } \
255             }
256              
257             /*
258             * Magic that we attach to suspended CVs, that contains state required to restore
259             * them
260             */
261              
262             static int suspendedstate_free(pTHX_ SV *sv, MAGIC *mg);
263              
264             static MGVTBL vtbl_suspendedstate = {
265             NULL, /* get */
266             NULL, /* set */
267             NULL, /* len */
268             NULL, /* clear */
269             suspendedstate_free,
270             };
271              
272             #ifdef HAVE_DMD_HELPER
273             static int dumpmagic_suspendedstate(pTHX_ DMDContext *ctx, const SV *sv, MAGIC *mg)
274             {
275             SuspendedState *state = (SuspendedState *)mg->mg_ptr;
276             int ret = 0;
277              
278             ret += DMD_ANNOTATE_SV(sv, state->awaiting_future, "the awaiting Future");
279             ret += DMD_ANNOTATE_SV(sv, state->returning_future, "the returning Future");
280              
281             SuspendedFrame *frame;
282             for(frame = state->frames; frame; frame = frame->next) {
283             int i;
284              
285             for(i = 0; i < frame->stacklen; i++)
286             ret += DMD_ANNOTATE_SV(sv, frame->stack[i], "a suspended stack temporary");
287              
288             for(i = 0; i < frame->mortallen; i++)
289             ret += DMD_ANNOTATE_SV(sv, frame->mortals[i], "a suspended mortal");
290              
291             #ifdef HAVE_ITERVAR
292             if(frame->itervar)
293             ret += DMD_ANNOTATE_SV(sv, frame->itervar, "a suspended loop iteration variable");
294             #endif
295              
296             switch(frame->type) {
297             case CXt_BLOCK:
298             case CXt_LOOP_PLAIN:
299             break;
300              
301             case CXt_LOOP_LAZYSV:
302             ret += DMD_ANNOTATE_SV(sv, frame->el.loop.state_u.lazysv.cur, "a suspended foreach LAZYSV loop iterator value");
303             ret += DMD_ANNOTATE_SV(sv, frame->el.loop.state_u.lazysv.end, "a suspended foreach LAZYSV loop stop value");
304             goto cxt_loop_common;
305              
306             #if HAVE_PERL_VERSION(5, 24, 0)
307             case CXt_LOOP_ARY:
308             #else
309             case CXt_LOOP_FOR:
310             #endif
311             if(frame->el.loop.state_u.ary.ary)
312             ret += DMD_ANNOTATE_SV(sv, (SV *)frame->el.loop.state_u.ary.ary, "a suspended foreach ARY loop value array");
313             goto cxt_loop_common;
314              
315             case CXt_LOOP_LAZYIV:
316             #if HAVE_PERL_VERSION(5, 24, 0)
317             case CXt_LOOP_LIST:
318             #endif
319             cxt_loop_common:
320             #if !defined(HAVE_ITERVAR)
321             ret += DMD_ANNOTATE_SV(sv, frame->el.loop.itersave, "a suspended loop saved iteration variable");
322             #endif
323             break;
324             }
325              
326             for(i = 0; i < frame->savedlen; i++) {
327             struct Saved *saved = &frame->saved[i];
328             switch(saved->type) {
329             #ifdef SAVEt_CLEARPADRANGE
330             case SAVEt_CLEARPADRANGE:
331             #endif
332             case SAVEt_CLEARSV:
333             case SAVEt_INT_SMALL:
334             case SAVEt_DESTRUCTOR_X:
335             #ifdef SAVEt_STRLEN
336             case SAVEt_STRLEN:
337             #endif
338             case SAVEt_SET_SVFLAGS:
339             /* Nothing interesting */
340             break;
341              
342             case SAVEt_FREEPV:
343             /* This is interesting but a plain char* pointer so there's nothing
344             * we can do with it in Devel::MAT */
345             break;
346              
347             case SAVEt_COMPPAD:
348             ret += DMD_ANNOTATE_SV(sv, saved->cur.ptr, "a suspended SAVEt_COMPPAD");
349             break;
350              
351             case SAVEt_FREESV:
352             ret += DMD_ANNOTATE_SV(sv, saved->saved.sv, "a suspended SAVEt_FREESV");
353             break;
354              
355             case SAVEt_SV:
356             ret += DMD_ANNOTATE_SV(sv, (SV *)saved->u.gv, "a suspended SAVEt_SV target GV");
357             ret += DMD_ANNOTATE_SV(sv, saved->cur.sv, "a suspended SAVEt_SV current value");
358             ret += DMD_ANNOTATE_SV(sv, saved->saved.sv, "a suspended SAVEt_SV saved value");
359             break;
360              
361             case SAVEt_SPTR:
362             ret += DMD_ANNOTATE_SV(sv, saved->cur.sv, "a suspended SAVEt_SPTR current value");
363             ret += DMD_ANNOTATE_SV(sv, saved->saved.sv, "a suspended SAVEt_SPTR saved value");
364             break;
365              
366             case SAVEt_PADSV_AND_MORTALIZE:
367             ret += DMD_ANNOTATE_SV(sv, saved->cur.sv, "a suspended SAVEt_PADSV_AND_MORTALIZE current value");
368             ret += DMD_ANNOTATE_SV(sv, saved->saved.sv, "a suspended SAVEt_PADSV_AND_MORTALIZE saved value");
369             break;
370             }
371             }
372             }
373              
374             if(state->padlen && state->padslots) {
375             int i;
376             for(i = 0; i < state->padlen - 1; i++)
377             if(state->padslots[i])
378             ret += DMD_ANNOTATE_SV(sv, state->padslots[i], "a suspended pad slot");
379             }
380              
381             if(state->defav)
382             ret += DMD_ANNOTATE_SV(sv, (SV *)state->defav, "the subroutine arguments AV");
383              
384             if(state->modhookdata)
385             ret += DMD_ANNOTATE_SV(sv, (SV *)state->modhookdata, "the module hook data HV");
386              
387             return ret;
388             }
389             #endif
390              
391             #define suspendedstate_get(cv) MY_suspendedstate_get(aTHX_ cv)
392 370           static SuspendedState *MY_suspendedstate_get(pTHX_ CV *cv)
393             {
394             MAGIC *magic;
395              
396 370 100         for(magic = mg_find((SV *)cv, PERL_MAGIC_ext); magic; magic = magic->mg_moremagic)
397 238 50         if(magic->mg_type == PERL_MAGIC_ext && magic->mg_virtual == &vtbl_suspendedstate)
    50          
398 238           return (SuspendedState *)magic->mg_ptr;
399              
400             return NULL;
401             }
402              
403             #define suspendedstate_new(cv) MY_suspendedstate_new(aTHX_ cv)
404 94           static SuspendedState *MY_suspendedstate_new(pTHX_ CV *cv)
405             {
406             SuspendedState *ret;
407 94           Newx(ret, 1, SuspendedState);
408              
409 94           ret->awaiting_future = NULL;
410 94           ret->returning_future = NULL;
411 94           ret->frames = NULL;
412 94           ret->padslots = NULL;
413 94           ret->modhookdata = NULL;
414 94           ret->defav = NULL;
415              
416 94           sv_magicext((SV *)cv, NULL, PERL_MAGIC_ext, &vtbl_suspendedstate, (char *)ret, 0);
417              
418 94           return ret;
419             }
420              
421 93           static int suspendedstate_free(pTHX_ SV *sv, MAGIC *mg)
422             {
423 93           SuspendedState *state = (SuspendedState *)mg->mg_ptr;
424              
425 93 100         if(state->awaiting_future) {
426             SvREFCNT_dec(state->awaiting_future);
427 11           state->awaiting_future = NULL;
428             }
429              
430 93 100         if(state->returning_future) {
431             SvREFCNT_dec(state->returning_future);
432 11           state->returning_future = NULL;
433             }
434              
435 93 100         if(state->frames) {
436             SuspendedFrame *frame, *next = state->frames;
437 24 100         while((frame = next)) {
438 13           next = frame->next;
439              
440 13 100         if(frame->stacklen) {
441             /* The stack isn't refcounted, so we should not SvREFCNT_dec() these
442             * items
443             */
444 3           Safefree(frame->stack);
445             }
446              
447 13 100         if(frame->marklen) {
448 3           Safefree(frame->marks);
449             }
450              
451 13 100         if(frame->saved) {
452             int idx;
453 12 100         for(idx = 0; idx < frame->savedlen; idx++) {
454 8           struct Saved *saved = &frame->saved[idx];
455 8           switch(saved->type) {
456             /* Saved types for which we've no cleanup needed */
457             #ifdef SAVEt_CLEARPADRANGE
458             case SAVEt_CLEARPADRANGE:
459             #endif
460             case SAVEt_CLEARSV:
461             case SAVEt_COMPPAD:
462             case SAVEt_INT_SMALL:
463             case SAVEt_DESTRUCTOR_X:
464             #ifdef SAVEt_STRLEN
465             case SAVEt_STRLEN:
466             #endif
467             case SAVEt_SET_SVFLAGS:
468             break;
469              
470             case SAVEt_FREEPV:
471 0           Safefree(saved->cur.ptr);
472 0           break;
473              
474             case SAVEt_FREESV:
475 0           SvREFCNT_dec(saved->saved.sv);
476             break;
477              
478             case SAVEt_SV:
479 1           SvREFCNT_dec(saved->u.gv);
480 1           SvREFCNT_dec(saved->saved.sv);
481 1           SvREFCNT_dec(saved->cur.sv);
482             break;
483              
484             case SAVEt_PADSV_AND_MORTALIZE:
485 0           SvREFCNT_dec(saved->saved.sv);
486 0           SvREFCNT_dec(saved->cur.sv);
487             break;
488              
489             case SAVEt_SPTR:
490 0           SvREFCNT_dec(saved->saved.sv);
491             /* saved->cur.sv does not account for an extra refcount */
492             break;
493              
494             default:
495             {
496 0           char *name = PL_savetype_name[saved->type];
497 0 0         if(name)
498 0           fprintf(stderr, "TODO: free saved slot type SAVEt_%s=%d\n", name, saved->type);
499             else
500 0           fprintf(stderr, "TODO: free saved slot type UNKNOWN=%d\n", saved->type);
501             break;
502             }
503             }
504             }
505              
506 4           Safefree(frame->saved);
507             }
508              
509 13           switch(frame->type) {
510             case CXt_BLOCK:
511             case CXt_LOOP_PLAIN:
512             break;
513              
514             case CXt_LOOP_LAZYSV:
515 0           SvREFCNT_dec(frame->el.loop.state_u.lazysv.cur);
516 0           SvREFCNT_dec(frame->el.loop.state_u.lazysv.end);
517             goto cxt_loop_common;
518              
519             #if HAVE_PERL_VERSION(5, 24, 0)
520             case CXt_LOOP_ARY:
521             #else
522             case CXt_LOOP_FOR:
523             #endif
524 0 0         if(frame->el.loop.state_u.ary.ary)
525             SvREFCNT_dec(frame->el.loop.state_u.ary.ary);
526             goto cxt_loop_common;
527              
528             case CXt_LOOP_LAZYIV:
529             #if HAVE_PERL_VERSION(5, 24, 0)
530             case CXt_LOOP_LIST:
531             #endif
532             cxt_loop_common:
533             #if !defined(HAVE_ITERVAR)
534 1           SvREFCNT_dec(frame->el.loop.itersave);
535             #endif
536             break;
537             }
538              
539             #ifdef HAVE_ITERVAR
540             if(frame->itervar) {
541             SvREFCNT_dec(frame->itervar);
542             frame->itervar = NULL;
543             }
544             #endif
545              
546 13 100         if(frame->mortals) {
547             int i;
548 4 100         for(i = 0; i < frame->mortallen; i++)
549 2           sv_2mortal(frame->mortals[i]);
550              
551 2           Safefree(frame->mortals);
552             }
553              
554 13           Safefree(frame);
555             }
556             }
557              
558 93 100         if(state->padslots) {
559             int i;
560 50 100         for(i = 0; i < state->padlen - 1; i++) {
561 39 100         if(state->padslots[i])
562             SvREFCNT_dec(state->padslots[i]);
563             }
564              
565 11           Safefree(state->padslots);
566 11           state->padslots = NULL;
567 11           state->padlen = 0;
568             }
569              
570 93 100         if(state->defav) {
571             /* This effectively held two references; one for GvAV(PL_defgv) and one
572             * for PAD_SV(0) */
573             SvREFCNT_dec(state->defav);
574 11           SvREFCNT_dec(state->defav);
575 11           state->defav = NULL;
576             }
577              
578 93 100         if(state->modhookdata) {
579 3           struct HookRegistrations *regs = registrations(FALSE);
580             /* New hooks first */
581 3 50         if(regs)
582 0 0         RUN_HOOKS_REV(free, (CV *)sv, state->modhookdata);
    0          
583              
584             /* Legacy hooks after */
585 3           SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", FALSE);
586 3 50         if(hookp && SvOK(*hookp) && SvUV(*hookp)) {
    0          
    0          
    0          
    0          
    0          
587 0           warn("Invoking legacy Future::AsyncAwait suspendhook for FREE phase");
588 0 0         SuspendHookFunc *hook = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
589 0           (*hook)(aTHX_ FAA_PHASE_FREE, (CV *)sv, state->modhookdata);
590             }
591              
592 3           SvREFCNT_dec(state->modhookdata);
593             }
594              
595 93           Safefree(state);
596              
597 93           return 1;
598             }
599              
600             #define suspend_frame(frame, cx) MY_suspend_frame(aTHX_ frame, cx)
601 166           static void MY_suspend_frame(pTHX_ SuspendedFrame *frame, PERL_CONTEXT *cx)
602             {
603 166           frame->stacklen = (I32)(PL_stack_sp - PL_stack_base) - cx->blk_oldsp;
604 166 100         if(frame->stacklen) {
605 30           SV **bp = PL_stack_base + cx->blk_oldsp + 1;
606             I32 i;
607             /* Steal SVs right off the stack */
608 30 50         Newx(frame->stack, frame->stacklen, SV *);
609 93 100         for(i = 0; i < frame->stacklen; i++) {
610 63           frame->stack[i] = bp[i];
611 63           bp[i] = NULL;
612             }
613 30           PL_stack_sp = PL_stack_base + cx->blk_oldsp;
614             }
615              
616 166           frame->marklen = (I32)(PL_markstack_ptr - PL_markstack) - cx->blk_oldmarksp;
617 166 100         if(frame->marklen) {
618 19           I32 *markbase = PL_markstack + cx->blk_oldmarksp + 1;
619             I32 i;
620 19 50         Newx(frame->marks, frame->marklen, I32);
621 40 100         for(i = 0; i < frame->marklen; i++) {
622             /* Translate mark value relative to base */
623 21           I32 relmark = markbase[i] - cx->blk_oldsp;
624 21           frame->marks[i] = relmark;
625             }
626 19           PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp;
627             }
628              
629 166           frame->oldcop = cx->blk_oldcop;
630              
631 166           I32 old_saveix = OLDSAVEIX(cx);
632             /* This is an over-estimate but it doesn't matter. We just waste a bit of RAM
633             * temporarily
634             */
635 166           I32 savedlen = PL_savestack_ix - old_saveix;
636 166 100         if(savedlen)
637 48 50         Newx(frame->saved, savedlen, struct Saved);
638             else
639 118           frame->saved = NULL;
640 166           frame->savedlen = 0; /* we increment it as we fill it */
641              
642             I32 oldtmpsfloor = -2;
643             #if HAVE_PERL_VERSION(5, 24, 0)
644             /* Perl 5.24 onwards has a PERL_CONTEXT slot for the old value of
645             * PL_tmpsfloor. Older perls do not, and keep it in the save stack instead.
646             * We'll keep an eye out for its saved value
647             */
648 166           oldtmpsfloor = cx->blk_old_tmpsfloor;
649             #endif
650              
651 230 100         while(PL_savestack_ix > old_saveix) {
652             /* Useful references
653             * scope.h
654             * scope.c: Perl_leave_scope()
655             */
656              
657 64           UV uv = PL_savestack[PL_savestack_ix-1].any_uv;
658 64           U8 type = (U8)uv & SAVE_MASK;
659              
660 64           struct Saved *saved = &frame->saved[frame->savedlen];
661              
662 64           switch(type) {
663             #ifdef SAVEt_CLEARPADRANGE
664             case SAVEt_CLEARPADRANGE: {
665 11           UV padix = uv >> (OPpPADRANGE_COUNTSHIFT + SAVE_TIGHT_SHIFT);
666 11           I32 count = (uv >> SAVE_TIGHT_SHIFT) & OPpPADRANGE_COUNTMASK;
667 11           PL_savestack_ix--;
668              
669 11 100         saved->type = count == 1 ? SAVEt_CLEARSV : SAVEt_CLEARPADRANGE;
670 11           saved->u.clearpad.padix = padix;
671 11           saved->u.clearpad.count = count;
672              
673 11           break;
674             }
675             #endif
676              
677             case SAVEt_CLEARSV: {
678 41           UV padix = (uv >> SAVE_TIGHT_SHIFT);
679 41           PL_savestack_ix--;
680              
681 41           saved->type = SAVEt_CLEARSV;
682 41           saved->u.clearpad.padix = padix;
683              
684 41           break;
685             }
686              
687             case SAVEt_COMPPAD: {
688             /* This occurs as a side-effect of Perl_pad_new on 5.22 */
689 0           PL_savestack_ix -= 2;
690 0           void *pad = PL_savestack[PL_savestack_ix].any_ptr;
691              
692 0           saved->type = SAVEt_COMPPAD;
693 0           saved->saved.ptr = pad;
694 0           saved->cur.ptr = PL_comppad;
695              
696 0           PL_comppad = pad;
697 0 0         PL_curpad = PL_comppad ? AvARRAY(PL_comppad) : NULL;
698              
699 0           break;
700             }
701              
702             case SAVEt_FREEPV: {
703 0           PL_savestack_ix -= 2;
704 0           char *pv = PL_savestack[PL_savestack_ix].any_ptr;
705              
706 0           saved->type = SAVEt_FREEPV;
707 0           saved->saved.ptr = pv;
708              
709 0           break;
710             }
711              
712             case SAVEt_FREESV: {
713 1           PL_savestack_ix -= 2;
714 1           void *sv = PL_savestack[PL_savestack_ix].any_ptr;
715              
716 1           saved->type = SAVEt_FREESV;
717 1           saved->saved.sv = sv;
718              
719 1           break;
720             }
721              
722             case SAVEt_INT_SMALL: {
723 0           PL_savestack_ix -= 2;
724 0           int val = ((int)uv >> SAVE_TIGHT_SHIFT);
725 0           int *var = PL_savestack[PL_savestack_ix].any_ptr;
726              
727             /* In general we don't want to support this; but specifically on perls
728             * older than 5.20, this might be PL_tmps_floor
729             */
730 0 0         if(var == (int *)&PL_tmps_floor) {
731             /* Don't bother to save the old tmpsfloor as we'll SAVETMPS again
732             * later if we need to
733             */
734             oldtmpsfloor = val;
735             goto nosave;
736             }
737              
738 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_INT_SMALL with var != &PL_tmps_floor\n");
739 0           break;
740             }
741              
742             case SAVEt_DESTRUCTOR_X: {
743             /* This is only known to be used by Syntax::Keyword::Try to implement
744             * finally blocks. It may be found elsewhere for which this code is
745             * unsafe, but detecting such cases is generally impossible. Good luck.
746             */
747 2           PL_savestack_ix -= 3;
748 2           void (*func)(pTHX_ void *) = PL_savestack[PL_savestack_ix].any_dxptr;
749 2           void *data = PL_savestack[PL_savestack_ix+1].any_ptr;
750              
751 2           saved->type = SAVEt_DESTRUCTOR_X;
752 2           saved->u.dx.func = func;
753 2           saved->u.dx.data = data;
754              
755 2           break;
756             }
757              
758             case SAVEt_ITEM: {
759 0           PL_savestack_ix -= 3;
760 0           SV *var = PL_savestack[PL_savestack_ix].any_ptr;
761 0           SV *val = PL_savestack[PL_savestack_ix+1].any_ptr;
762              
763 0           saved->type = SAVEt_ITEM;
764 0           saved->u.sv = var;
765 0           saved->cur.sv = newSVsv(var);
766 0           saved->saved.sv = val;
767              
768             /* restore it for now */
769 0           sv_setsv(var, val);
770              
771 0           break;
772             }
773              
774             case SAVEt_SPTR: {
775 0           PL_savestack_ix -= 3;
776 0           SV *val = PL_savestack[PL_savestack_ix].any_ptr;
777 0           SV **var = PL_savestack[PL_savestack_ix+1].any_ptr;
778              
779             /* In general we don't support this; but specifically we will accept
780             * it if we can convert var into a PAD index. This is to support
781             * SAVESPTR(PAD_SVl(padix)), as may be used by Object::Pad or others
782             */
783 0 0         if(var < PL_curpad || var > PL_curpad + AvFILL(PL_comppad))
    0          
    0          
784 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_SPTR with var not the current pad\n");
785              
786 0           PADOFFSET padix = var - PL_curpad;
787              
788 0           saved->type = SAVEt_SPTR;
789 0           saved->u.padix = padix;
790 0           saved->cur.sv = PL_curpad[padix]; /* steal ownership */
791 0           saved->saved.sv = val; /* steal ownership */
792              
793             /* restore it for now */
794 0           PL_curpad[padix] = SvREFCNT_inc(val);
795              
796 0           break;
797             }
798              
799             #ifdef SAVEt_STRLEN
800             case SAVEt_STRLEN: {
801 0           PL_savestack_ix -= 3;
802 0           STRLEN val = PL_savestack[PL_savestack_ix].any_iv;
803 0           STRLEN *var = PL_savestack[PL_savestack_ix+1].any_ptr;
804              
805             /* In general we don't want to support this; but specifically on perls
806             * older than 5.24, this might be PL_tmps_floor
807             */
808 0 0         if(var == (STRLEN *)&PL_tmps_floor) {
809             /* Don't bother to save the old tmpsfloor as we'll SAVETMPS again
810             * later if we need to
811             */
812 0           oldtmpsfloor = val;
813 0           goto nosave;
814             }
815              
816 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_STRLEN with var != &PL_tmps_floor\n");
817 0           break;
818             }
819             #endif
820              
821             case SAVEt_SV: {
822 9           PL_savestack_ix -= 3;
823             /* despite being called SAVEt_SV, the first field actually points at
824             * the GV containing the local'ised SV
825             */
826 9           GV *gv = PL_savestack[PL_savestack_ix ].any_ptr;
827 9           SV *val = PL_savestack[PL_savestack_ix+1].any_ptr;
828              
829             /* In general we don't want to support local $VAR. However, a special
830             * case of local $@ is allowable
831             * See also https://rt.cpan.org/Ticket/Display.html?id=122793
832             */
833 9 50         if(gv != PL_errgv) {
834 0           const char *name = GvNAME(gv);
835 0 0         const char *stashname = HvNAME(GvSTASH(gv));
    0          
    0          
    0          
    0          
    0          
836              
837 0 0         if(name && stashname)
838 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_SV with gv != PL_errgv ($%s::%s)\n",
839             stashname, name);
840             else
841 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_SV with gv != PL_errgv\n");
842             }
843              
844 9           saved->type = SAVEt_SV;
845 9           saved->u.gv = gv;
846 9           saved->cur.sv = GvSV(gv); /* steal ownership */
847 9           saved->saved.sv = val; /* steal ownership */
848              
849             /* restore it for now */
850 9           GvSV(gv) = val;
851              
852 9           break;
853             }
854              
855             case SAVEt_PADSV_AND_MORTALIZE: {
856 0           PL_savestack_ix -= 4;
857 0           SV *val = PL_savestack[PL_savestack_ix ].any_ptr;
858 0           AV *padav = PL_savestack[PL_savestack_ix+1].any_ptr;
859 0           PADOFFSET padix = PL_savestack[PL_savestack_ix+2].any_uv;
860              
861 0 0         if(padav != PL_comppad)
862 0           panic("TODO: Unsure how to handle a savestack entry of SAVEt_PADSV_AND_MORTALIZE with padav != PL_comppad\n");
863              
864 0           SvREFCNT_inc(PL_curpad[padix]); /* un-mortalize */
865              
866 0           saved->type = SAVEt_PADSV_AND_MORTALIZE;
867 0           saved->u.padix = padix;
868 0           saved->cur.sv = PL_curpad[padix]; /* steal ownership */
869 0           saved->saved.sv = val; /* steal ownership */
870              
871 0           AvARRAY(padav)[padix] = SvREFCNT_inc(val);
872              
873 0           break;
874             }
875              
876             case SAVEt_SET_SVFLAGS: {
877 0           PL_savestack_ix -= 4;
878 0           SV *sv = PL_savestack[PL_savestack_ix ].any_ptr;
879 0           U32 mask = (U32)PL_savestack[PL_savestack_ix+1].any_i32;
880 0           U32 set = (U32)PL_savestack[PL_savestack_ix+2].any_i32;
881              
882 0           saved->type = SAVEt_SET_SVFLAGS;
883 0           saved->u.svflags.sv = sv;
884 0           saved->u.svflags.mask = mask;
885 0           saved->u.svflags.set = set;
886              
887 0           break;
888             }
889              
890             default:
891             {
892 0           char *name = PL_savetype_name[type];
893 0 0         if(name)
894 0           panic("TODO: Unsure how to handle savestack entry of SAVEt_%s=%d\n", name, type);
895             else
896 0           panic("TODO: Unsure how to handle savestack entry of UNKNOWN=%d\n", type);
897             }
898             }
899              
900 64           frame->savedlen++;
901              
902             nosave:
903             ;
904             }
905              
906 166 50         if(OLDSAVEIX(cx) != PL_savestack_ix)
907 0           panic("TODO: handle OLDSAVEIX\n");
908              
909 166           frame->scopes = (PL_scopestack_ix - cx->blk_oldscopesp) + 1;
910 166 100         if(frame->scopes) {
911             /* We'll mutate PL_scopestack_ix but it doesn't matter as dounwind() will
912             * put it right at the end. Do this unconditionally to avoid divergent
913             * behaviour between -DDEBUGGING builds and non.
914             */
915 119           PL_scopestack_ix -= frame->scopes;
916             }
917              
918             /* ref:
919             * https://perl5.git.perl.org/perl.git/blob/HEAD:/cop.h
920             */
921 166           U8 type = CxTYPE(cx);
922 166           switch(type) {
923             case CXt_BLOCK:
924 9           frame->type = CXt_BLOCK;
925 9           frame->gimme = cx->blk_gimme;
926             /* nothing else special */
927 9           break;
928              
929             case CXt_LOOP_PLAIN:
930 8           frame->type = type;
931 8           frame->el.loop = cx->blk_loop;
932 8           frame->gimme = cx->blk_gimme;
933 8           break;
934              
935             #if HAVE_PERL_VERSION(5, 24, 0)
936             case CXt_LOOP_ARY:
937             case CXt_LOOP_LIST:
938             #else
939             case CXt_LOOP_FOR:
940             #endif
941             case CXt_LOOP_LAZYSV:
942             case CXt_LOOP_LAZYIV:
943 25 50         if(!CxPADLOOP(cx))
944             /* non-lexical foreach will effectively work like 'local' and we
945             * can't really support local
946             */
947 0           croak("Cannot suspend a foreach loop on non-lexical iterator");
948              
949 25           frame->type = type;
950 25           frame->el.loop = cx->blk_loop;
951 25           frame->gimme = cx->blk_gimme;
952              
953             #ifdef HAVE_ITERVAR
954             # ifdef USE_ITHREADS
955             if(cx->blk_loop.itervar_u.svp != (SV **)PL_comppad)
956             panic("TODO: Unsure how to handle a foreach loop with itervar != PL_comppad\n");
957             # else
958             if(cx->blk_loop.itervar_u.svp != &PAD_SVl(cx->blk_loop.my_op->op_targ))
959             panic("TODO: Unsure how to handle a foreach loop with itervar != PAD_SVl(op_targ))\n");
960             # endif
961              
962             frame->itervar = SvREFCNT_inc(*CxITERVAR(cx));
963             #else
964 25 50         if(CxITERVAR(cx) != &PAD_SVl(cx->blk_loop.my_op->op_targ))
    0          
    50          
965 0           panic("TODO: Unsure how to handle a foreach loop with itervar != PAD_SVl(op_targ))\n");
966 25           SvREFCNT_inc(cx->blk_loop.itersave);
967             #endif
968              
969 25           switch(type) {
970             case CXt_LOOP_LAZYSV:
971             /* these two fields are refcounted, so we need to save them from
972             * dounwind() throwing them away
973             */
974 3           SvREFCNT_inc(frame->el.loop.state_u.lazysv.cur);
975 3           SvREFCNT_inc(frame->el.loop.state_u.lazysv.end);
976             break;
977              
978             #if HAVE_PERL_VERSION(5, 24, 0)
979             case CXt_LOOP_ARY:
980             #else
981             case CXt_LOOP_FOR:
982             /* The ix field stores an absolute stack height as offset from
983             * PL_stack_base directly. When we get resumed the stack will
984             * probably not be the same absolute height at this point, so we'll
985             * have to store them relative to something fixed.
986             */
987             if(!cx->blk_loop.state_u.ary.ary) {
988             I32 height = PL_stack_sp - PL_stack_base;
989             frame->el.loop.state_u.ary.ix = height - frame->el.loop.state_u.ary.ix;
990             }
991             #endif
992             /* this field is also refcounted, so we need to save it too */
993 3 50         if(frame->el.loop.state_u.ary.ary)
994             SvREFCNT_inc(frame->el.loop.state_u.ary.ary);
995             break;
996              
997             #if HAVE_PERL_VERSION(5, 24, 0)
998             case CXt_LOOP_LIST: {
999             /* The various fields in the context structure store absolute stack
1000             * heights as offsets from PL_stack_base directly. When we get
1001             * resumed the stack will probably not be the same absolute height
1002             * at this point, so we'll have to store them relative to something
1003             * fixed.
1004             * We'll adjust them to be upside-down, counting -backwards- from
1005             * the current stack height.
1006             */
1007 16           I32 height = PL_stack_sp - PL_stack_base;
1008              
1009 16 50         if(cx->blk_oldsp != height)
1010 0           panic("ARGH suspending CXt_LOOP_LIST frame with blk_oldsp != stack height\n");
1011              
1012             /* First item is at [1] oddly, not [0] */
1013 16           frame->loop_list_first_item = PL_stack_base[cx->blk_loop.state_u.stack.basesp+1];
1014              
1015 16           frame->el.loop.state_u.stack.basesp = height - frame->el.loop.state_u.stack.basesp;
1016 16           frame->el.loop.state_u.stack.ix = height - frame->el.loop.state_u.stack.ix;
1017 16           break;
1018             }
1019             #endif
1020             }
1021              
1022             break;
1023              
1024             case CXt_EVAL: {
1025 124 50         if(!(cx->cx_type & CXp_TRYBLOCK))
1026 0           panic("TODO: handle CXt_EVAL without CXp_TRYBLOCK\n");
1027 124 50         if(cx->blk_eval.old_namesv)
1028 0           panic("TODO: handle cx->blk_eval.old_namesv\n");
1029 124 50         if(cx->blk_eval.cv)
1030 0           panic("TODO: handle cx->blk_eval.cv\n");
1031 124 50         if(cx->blk_eval.cur_top_env != PL_top_env)
1032 0           panic("TODO: handle cx->blk_eval.cur_top_env\n");
1033              
1034             /*
1035             * It seems we don't need to care about blk_eval.old_eval_root or
1036             * blk_eval.cur_text, and if we ignore these then it works fine via
1037             * string eval().
1038             * https://rt.cpan.org/Ticket/Display.html?id=126036
1039             */
1040              
1041 124           frame->type = CXt_EVAL;
1042 124           frame->gimme = cx->blk_gimme;
1043              
1044             #ifdef HAVE_CX_TRY
1045             if(CxTRY(cx))
1046             frame->type |= CXp_TRY;
1047             #endif
1048              
1049 124           frame->el.eval.retop = cx->blk_eval.retop;
1050              
1051 124           break;
1052             }
1053              
1054             default:
1055 0           panic("TODO: unsure how to handle a context frame of type %d\n", CxTYPE(cx));
1056             }
1057              
1058 166           frame->mortallen = 0;
1059 166           frame->mortals = NULL;
1060 166 50         if(oldtmpsfloor == -2) {
1061             /* Don't worry about it; the next level down will save us */
1062             }
1063             else {
1064             /* Save the mortals! */
1065 166           SV **tmpsbase = PL_tmps_stack + PL_tmps_floor + 1;
1066             I32 i;
1067              
1068 166           frame->mortallen = (I32)(PL_tmps_ix - PL_tmps_floor);
1069 166 100         if(frame->mortallen) {
1070 19 50         Newx(frame->mortals, frame->mortallen, SV *);
1071 44 100         for(i = 0; i < frame->mortallen; i++) {
1072 25           frame->mortals[i] = tmpsbase[i];
1073 25           tmpsbase[i] = NULL;
1074             }
1075             }
1076              
1077 166           PL_tmps_ix = PL_tmps_floor;
1078 166           PL_tmps_floor = oldtmpsfloor;
1079             }
1080 166           }
1081              
1082             #define suspendedstate_suspend(state, cv) MY_suspendedstate_suspend(aTHX_ state, cv)
1083 119           static void MY_suspendedstate_suspend(pTHX_ SuspendedState *state, CV *cv)
1084             {
1085             I32 cxix;
1086             PADOFFSET padnames_max, pad_max, i;
1087             PADLIST *plist;
1088             PADNAME **padnames;
1089             PAD *pad;
1090             SV **padsvs;
1091              
1092 119           state->frames = NULL;
1093              
1094 285 50         for(cxix = cxstack_ix; cxix; cxix--) {
1095 285           PERL_CONTEXT *cx = &cxstack[cxix];
1096 285 100         if(CxTYPE(cx) == CXt_SUB)
1097             break;
1098              
1099             SuspendedFrame *frame;
1100              
1101 166           Newx(frame, 1, SuspendedFrame);
1102 166           frame->next = state->frames;
1103 166           state->frames = frame;
1104             #ifdef HAVE_ITERVAR
1105             frame->itervar = NULL;
1106             #endif
1107              
1108 166           suspend_frame(frame, cx);
1109             }
1110              
1111             /* Now steal the lexical SVs from the PAD */
1112 119           plist = CvPADLIST(cv);
1113              
1114 119           padnames = PadnamelistARRAY(PadlistNAMES(plist));
1115 119           padnames_max = PadnamelistMAX(PadlistNAMES(plist));
1116              
1117 119           pad = PadlistARRAY(plist)[CvDEPTH(cv)];
1118 119           pad_max = PadMAX(pad);
1119 119           padsvs = PadARRAY(pad);
1120              
1121 119           state->padlen = PadMAX(pad) + 1;
1122 119           Newx(state->padslots, state->padlen - 1, SV *);
1123              
1124             /* slot 0 is always the @_ AV */
1125 460 100         for(i = 1; i <= pad_max; i++) {
1126 341 50         PADNAME *pname = (i <= padnames_max) ? padnames[i] : NULL;
1127              
1128 341 100         if(!padname_is_normal_lexical(pname)) {
1129 239           state->padslots[i-1] = NULL;
1130 239           continue;
1131             }
1132              
1133 102 50         if(PadnameIsSTATE(pname)) {
1134 0           state->padslots[i-1] = SvREFCNT_inc(padsvs[i]);
1135             }
1136             else {
1137             /* Don't fiddle refcount */
1138 102           state->padslots[i-1] = padsvs[i];
1139 102           switch(PadnamePV(pname)[0]) {
1140             case '@':
1141 15           padsvs[i] = MUTABLE_SV(newAV());
1142             break;
1143             case '%':
1144 5           padsvs[i] = MUTABLE_SV(newHV());
1145             break;
1146             case '$':
1147 82           padsvs[i] = newSV(0);
1148             break;
1149             default:
1150 0           panic("TODO: unsure how to steal and switch pad slot with pname %s\n",
1151             PadnamePV(pname));
1152             }
1153 102           SvPADMY_on(padsvs[i]);
1154             }
1155             }
1156              
1157 119 100         if(PL_curpm)
1158 2           state->curpm = PL_curpm;
1159             else
1160 117           state->curpm = NULL;
1161              
1162             #if !HAVE_PERL_VERSION(5, 24, 0)
1163             /* perls before v5.24 will crash if we try to do this at all */
1164             if(0)
1165             #elif HAVE_PERL_VERSION(5, 36, 0)
1166             /* perls 5.36 onwards have CvSIGNATURE; we don't need to bother doing this
1167             * inside signatured subs */
1168             if(!CvSIGNATURE(cv))
1169             #endif
1170             /* on perl versions between those, just do it unconditionally */
1171             {
1172             assert((AV *)PAD_SVl(0) == GvAV(PL_defgv));
1173 119           state->defav = GvAV(PL_defgv); /* steal */
1174              
1175 119           AV *av = GvAV(PL_defgv) = newAV();
1176 238           PAD_SVl(0) = SvREFCNT_inc(av);
1177             }
1178              
1179 119           dounwind(cxix);
1180 119           }
1181              
1182             #define resume_frame(frame, cx) MY_resume_frame(aTHX_ frame)
1183 153           static void MY_resume_frame(pTHX_ SuspendedFrame *frame)
1184             {
1185             I32 i;
1186              
1187             PERL_CONTEXT *cx;
1188             I32 was_scopestack_ix = PL_scopestack_ix;
1189              
1190 153           switch(frame->type) {
1191             case CXt_BLOCK:
1192             #if !HAVE_PERL_VERSION(5, 24, 0)
1193             ENTER_with_name("block");
1194             SAVETMPS;
1195             #endif
1196 8           cx = cx_pushblock(CXt_BLOCK, frame->gimme, PL_stack_sp, PL_savestack_ix);
1197             /* nothing else special */
1198 8           break;
1199              
1200             case CXt_LOOP_PLAIN:
1201             #if !HAVE_PERL_VERSION(5, 24, 0)
1202             ENTER_with_name("loop1");
1203             SAVETMPS;
1204             ENTER_with_name("loop2");
1205             #endif
1206 8           cx = cx_pushblock(frame->type, frame->gimme, PL_stack_sp, PL_savestack_ix);
1207             /* don't call cx_pushloop_plain() because it will get this wrong */
1208 8           cx->blk_loop = frame->el.loop;
1209 8           break;
1210              
1211             #if HAVE_PERL_VERSION(5, 24, 0)
1212             case CXt_LOOP_ARY:
1213             case CXt_LOOP_LIST:
1214             #else
1215             case CXt_LOOP_FOR:
1216             #endif
1217             case CXt_LOOP_LAZYSV:
1218             case CXt_LOOP_LAZYIV:
1219             #if !HAVE_PERL_VERSION(5, 24, 0)
1220             ENTER_with_name("loop1");
1221             SAVETMPS;
1222             ENTER_with_name("loop2");
1223             #endif
1224 24           cx = cx_pushblock(frame->type, frame->gimme, PL_stack_sp, PL_savestack_ix);
1225             /* don't call cx_pushloop_plain() because it will get this wrong */
1226 24           cx->blk_loop = frame->el.loop;
1227             #if HAVE_PERL_VERSION(5, 24, 0)
1228 24           cx->cx_type |= CXp_FOR_PAD;
1229             #endif
1230              
1231             #ifdef HAVE_ITERVAR
1232             # ifdef USE_ITHREADS
1233             cx->blk_loop.itervar_u.svp = (SV **)PL_comppad;
1234             # else
1235             cx->blk_loop.itervar_u.svp = &PAD_SVl(cx->blk_loop.my_op->op_targ);
1236             # endif
1237             SvREFCNT_dec(*CxITERVAR(cx));
1238             *CxITERVAR(cx) = frame->itervar;
1239             frame->itervar = NULL;
1240             #else
1241 24           cx->blk_loop.itervar_u.svp = &PAD_SVl(cx->blk_loop.my_op->op_targ);
1242             #endif
1243 24           break;
1244              
1245             case CXt_EVAL:
1246 113 50         if(CATCH_GET)
1247 0           panic("Too late to docatch()\n");
1248              
1249             #if !HAVE_PERL_VERSION(5, 24, 0)
1250             ENTER_with_name("eval_scope");
1251             SAVETMPS;
1252             #endif
1253 113           cx = cx_pushblock(CXt_EVAL|CXp_TRYBLOCK, frame->gimme,
1254             PL_stack_sp, PL_savestack_ix);
1255 113           cx_pusheval(cx, frame->el.eval.retop, NULL);
1256 113           PL_in_eval = EVAL_INEVAL;
1257 113 50         CLEAR_ERRSV();
    50          
    50          
1258             break;
1259              
1260             #ifdef HAVE_CX_TRY
1261             case CXt_EVAL|CXp_TRY:
1262             if(CATCH_GET)
1263             panic("Too late to docatch()\n");
1264              
1265             cx = cx_pushblock(CXt_EVAL|CXp_EVALBLOCK|CXp_TRY, frame->gimme,
1266             PL_stack_sp, PL_savestack_ix);
1267             cx_pushtry(cx, frame->el.eval.retop);
1268             PL_in_eval = EVAL_INEVAL;
1269             CLEAR_ERRSV();
1270             break;
1271             #endif
1272              
1273             default:
1274 0           panic("TODO: Unsure how to restore a %d frame\n", frame->type);
1275             }
1276              
1277 153 100         if(frame->stacklen) {
1278 27           dSP;
1279 27 50         EXTEND(SP, frame->stacklen);
1280              
1281 85 100         for(i = 0; i < frame->stacklen; i++) {
1282 58           PUSHs(frame->stack[i]);
1283             }
1284              
1285 27           Safefree(frame->stack);
1286 27           PUTBACK;
1287             }
1288              
1289 153 100         if(frame->marklen) {
1290 34 100         for(i = 0; i < frame->marklen; i++) {
1291 18           I32 absmark = frame->marks[i] + cx->blk_oldsp;
1292 18 50         PUSHMARK(PL_stack_base + absmark);
1293             }
1294              
1295 16           Safefree(frame->marks);
1296             }
1297              
1298 153           cx->blk_oldcop = frame->oldcop;
1299              
1300 209 100         for(i = frame->savedlen - 1; i >= 0; i--) {
1301 56           struct Saved *saved = &frame->saved[i];
1302              
1303 56           switch(saved->type) {
1304             case SAVEt_CLEARSV:
1305 42           save_clearsv(PL_curpad + saved->u.clearpad.padix);
1306 42           break;
1307              
1308             #ifdef SAVEt_CLEARPADRANGE
1309             case SAVEt_CLEARPADRANGE:
1310 4           save_clearpadrange(saved->u.clearpad.padix, saved->u.clearpad.count);
1311 4           break;
1312             #endif
1313              
1314             case SAVEt_DESTRUCTOR_X:
1315 1           save_pushptrptr(saved->u.dx.func, saved->u.dx.data, saved->type);
1316 1           break;
1317              
1318             case SAVEt_COMPPAD:
1319 0           PL_comppad = saved->saved.ptr;
1320 0           save_pushptr(PL_comppad, saved->type);
1321              
1322 0           PL_comppad = saved->cur.ptr;
1323 0 0         PL_curpad = PL_comppad ? AvARRAY(PL_comppad) : NULL;
1324 0           break;
1325              
1326             case SAVEt_FREEPV:
1327 0           save_freepv(saved->saved.ptr);
1328 0           break;
1329              
1330             case SAVEt_FREESV:
1331 1           save_freesv(saved->saved.sv);
1332 1           break;
1333              
1334             case SAVEt_INT:
1335 0           *(saved->u.iptr) = saved->saved.i;
1336 0           save_int(saved->u.iptr);
1337              
1338 0           *(saved->u.iptr) = saved->cur.i;
1339 0           break;
1340              
1341             case SAVEt_SV:
1342 8           save_pushptrptr(saved->u.gv, SvREFCNT_inc(saved->saved.sv), SAVEt_SV);
1343              
1344 8           SvREFCNT_dec(GvSV(saved->u.gv));
1345 8           GvSV(saved->u.gv) = saved->cur.sv;
1346 8           break;
1347              
1348             case SAVEt_ITEM:
1349 0           save_pushptrptr(saved->u.sv, saved->saved.sv, SAVEt_ITEM);
1350              
1351 0           sv_setsv(saved->u.sv, saved->cur.sv);
1352 0           SvREFCNT_dec(saved->cur.sv);
1353             break;
1354              
1355             case SAVEt_SPTR:
1356 0           PL_curpad[saved->u.padix] = saved->saved.sv;
1357 0           SAVESPTR(PL_curpad[saved->u.padix]);
1358              
1359 0           SvREFCNT_dec(PL_curpad[saved->u.padix]);
1360 0           PL_curpad[saved->u.padix] = saved->cur.sv;
1361 0           break;
1362              
1363             #ifdef SAVEt_STRLEN
1364             case SAVEt_STRLEN:
1365 0           *(saved->u.lenptr) = saved->saved.len;
1366 0           Perl_save_strlen(aTHX_ saved->u.lenptr);
1367              
1368 0           *(saved->u.lenptr) = saved->cur.len;
1369 0           break;
1370             #endif
1371              
1372             case SAVEt_PADSV_AND_MORTALIZE:
1373 0           PL_curpad[saved->u.padix] = saved->saved.sv;
1374 0           save_padsv_and_mortalize(saved->u.padix);
1375              
1376 0           SvREFCNT_dec(PL_curpad[saved->u.padix]);
1377 0           PL_curpad[saved->u.padix] = saved->cur.sv;
1378 0           break;
1379              
1380             case SAVEt_SET_SVFLAGS:
1381             /*
1382             save_set_svflags(saved->u.svflags.sv,
1383             saved->u.svflags.mask, saved->u.svflags.set);
1384             */
1385             break;
1386              
1387             default:
1388 0           panic("TODO: Unsure how to restore a %d savestack entry\n", saved->type);
1389             }
1390             }
1391              
1392 153 100         if(frame->saved)
1393 44           Safefree(frame->saved);
1394              
1395             if(frame->scopes) {
1396             #ifdef DEBUG
1397             if(PL_scopestack_ix - was_scopestack_ix < frame->scopes) {
1398             fprintf(stderr, "TODO ARG still more scopes to ENTER\n");
1399             }
1400             #endif
1401             }
1402              
1403 153 100         if(frame->mortallen) {
1404 40 100         for(i = 0; i < frame->mortallen; i++) {
1405 23           sv_2mortal(frame->mortals[i]);
1406             }
1407              
1408 17           Safefree(frame->mortals);
1409 17           frame->mortals = NULL;
1410             }
1411              
1412 153 100         switch(frame->type) {
1413             #if !HAVE_PERL_VERSION(5, 24, 0)
1414             case CXt_LOOP_FOR:
1415             if(!cx->blk_loop.state_u.ary.ary) {
1416             I32 height = PL_stack_sp - PL_stack_base - frame->stacklen;
1417             cx->blk_loop.state_u.ary.ix = height - cx->blk_loop.state_u.ary.ix;
1418             }
1419             break;
1420             #endif
1421              
1422             #if HAVE_PERL_VERSION(5, 24, 0)
1423             case CXt_LOOP_LIST: {
1424 15           I32 height = PL_stack_sp - PL_stack_base - frame->stacklen;
1425              
1426 15           cx->blk_loop.state_u.stack.basesp = height - cx->blk_loop.state_u.stack.basesp;
1427 15           cx->blk_loop.state_u.stack.ix = height - cx->blk_loop.state_u.stack.ix;
1428              
1429             /* For consistency; check that the first SV in the list is in the right
1430             * place. If so we presume the others are
1431             */
1432 15 50         if(PL_stack_base[cx->blk_loop.state_u.stack.basesp+1] == frame->loop_list_first_item)
1433             break;
1434              
1435             /* First item is at [1] oddly, not [0] */
1436             #ifdef debug_sv_summary
1437             fprintf(stderr, "F:AA: consistency check resume LOOP_LIST with first=%p:",
1438             frame->loop_list_first_item);
1439             debug_sv_summary(frame->loop_list_first_item);
1440             fprintf(stderr, " stackitem=%p:", PL_stack_base[frame->el.loop.state_u.stack.basesp + 1]);
1441             debug_sv_summary(PL_stack_base[frame->el.loop.state_u.stack.basesp]);
1442             fprintf(stderr, "\n");
1443             #endif
1444 0           panic("ARGH CXt_LOOP_LIST consistency check failed\n");
1445 0           break;
1446             }
1447             #endif
1448             }
1449 153           }
1450              
1451             #define suspendedstate_resume(state, cv) MY_suspendedstate_resume(aTHX_ state, cv)
1452 108           static void MY_suspendedstate_resume(pTHX_ SuspendedState *state, CV *cv)
1453             {
1454             I32 i;
1455              
1456 108 50         if(state->padlen) {
1457 108           PAD *pad = PadlistARRAY(CvPADLIST(cv))[CvDEPTH(cv)];
1458             PADOFFSET i;
1459              
1460             /* slot 0 is always the @_ AV */
1461 410 100         for(i = 1; i < state->padlen; i++) {
1462 302 100         if(!state->padslots[i-1])
1463 215           continue;
1464              
1465 87           SvREFCNT_dec(PadARRAY(pad)[i]);
1466 87           PadARRAY(pad)[i] = state->padslots[i-1];
1467             }
1468              
1469 108           Safefree(state->padslots);
1470 108           state->padslots = NULL;
1471 108           state->padlen = 0;
1472             }
1473              
1474             SuspendedFrame *frame, *next;
1475 261 100         for(frame = state->frames; frame; frame = next) {
1476 153           next = frame->next;
1477              
1478 153           resume_frame(frame, cx);
1479              
1480 153           Safefree(frame);
1481             }
1482 108           state->frames = NULL;
1483              
1484 108 100         if(state->curpm)
1485 2           PL_curpm = state->curpm;
1486              
1487 108 50         if(state->defav) {
1488 108           SvREFCNT_dec(GvAV(PL_defgv));
1489 108           SvREFCNT_dec(PAD_SVl(0));
1490              
1491 108           GvAV(PL_defgv) = state->defav;
1492 108           PAD_SVl(0) = (SV *)state->defav;
1493 108           state->defav = NULL;
1494             }
1495 108           }
1496              
1497             #define suspendedstate_cancel(state) MY_suspendedstate_cancel(aTHX_ state)
1498 16           static void MY_suspendedstate_cancel(pTHX_ SuspendedState *state)
1499             {
1500             SuspendedFrame *frame;
1501 16 100         for(frame = state->frames; frame; frame = frame->next) {
1502             I32 i;
1503              
1504 12 100         for(i = frame->savedlen - 1; i >= 0; i--) {
1505 4           struct Saved *saved = &frame->saved[i];
1506              
1507 4 100         switch(saved->type) {
1508             case SAVEt_DESTRUCTOR_X:
1509             /* We have to run destructors to ensure that defer {} and try/finally
1510             * work correctly
1511             * https://rt.cpan.org/Ticket/Display.html?id=135351
1512             */
1513 1           (*saved->u.dx.func)(aTHX_ saved->u.dx.data);
1514             break;
1515             }
1516             }
1517             }
1518 8           }
1519              
1520             /*
1521             * Pre-creation assistance
1522             */
1523              
1524             enum {
1525             PRECREATE_CANCEL,
1526             PRECREATE_MODHOOKDATA,
1527             };
1528              
1529             #define get_precreate_padix() S_get_precreate_padix(aTHX)
1530 209           PADOFFSET S_get_precreate_padix(pTHX)
1531             {
1532 209 50         return SvUV(SvRV(*hv_fetchs(GvHV(PL_hintgv), "Future::AsyncAwait/*precreate_padix", 0)));
1533             }
1534              
1535             #define get_or_create_precreate_padix() S_get_or_create_precreate_padix(aTHX)
1536 4           PADOFFSET S_get_or_create_precreate_padix(pTHX)
1537             {
1538             SV *sv;
1539 4 50         PADOFFSET padix = SvUV(sv = SvRV(*hv_fetchs(GvHV(PL_hintgv), "Future::AsyncAwait/*precreate_padix", 0)));
1540 4 50         if(!padix) {
1541 4           padix = pad_add_name_pvs("@(Future::AsyncAwait/precancel)", 0, NULL, NULL);
1542 4           sv_setuv(sv, padix);
1543              
1544 4           PADOFFSET p2 = pad_add_name_pvs("%(Future::AsyncAwait/premodhookdata)", 0, NULL, NULL);
1545             assert(p2 == padix + PRECREATE_MODHOOKDATA);
1546             }
1547              
1548 4           return padix;
1549             }
1550              
1551             /*
1552             * Some Future class helper functions
1553             */
1554              
1555             #define future_classname() MY_future_classname(aTHX)
1556 23           static SV *MY_future_classname(pTHX)
1557             {
1558             /* cop_hints_fetch_* return a mortal copy so this is fine */
1559 23           SV *class = cop_hints_fetch_pvs(PL_curcop, "Future::AsyncAwait/future", 0);
1560 23 100         if(class == &PL_sv_placeholder)
1561 22           class = sv_2mortal(newSVpvn("Future", 6));
1562              
1563 23           return class;
1564             }
1565              
1566             #define future_done_from_stack(f, mark) MY_future_done_from_stack(aTHX_ f, mark)
1567 91           static SV *MY_future_done_from_stack(pTHX_ SV *f, SV **mark)
1568             {
1569 91           dSP;
1570             SV **svp;
1571              
1572 91 50         EXTEND(SP, 1);
1573              
1574 91           ENTER_with_name("future_done_from_stack");
1575 91           SAVETMPS;
1576              
1577 91 50         PUSHMARK(mark);
1578 91           SV **bottom = mark + 1;
1579             const char *method;
1580              
1581             /* splice the class name 'Future' in to the start of the stack */
1582              
1583 200 100         for (svp = SP; svp >= bottom; svp--) {
1584 109           *(svp+1) = *svp;
1585             }
1586              
1587 91 100         if(f) {
1588             assert(SvROK(f));
1589 71           *bottom = f;
1590             method = "AWAIT_DONE";
1591             }
1592             else {
1593 20           *bottom = future_classname();
1594             method = "AWAIT_NEW_DONE";
1595             }
1596 91           SP++;
1597 91           PUTBACK;
1598              
1599 91           call_method(method, G_SCALAR);
1600              
1601 91           SPAGAIN;
1602              
1603 91           SV *ret = SvREFCNT_inc(POPs);
1604              
1605 91 50         FREETMPS;
1606 91           LEAVE_with_name("future_done_from_stack");
1607              
1608 91           return ret;
1609             }
1610              
1611             #define future_fail(f, failure) MY_future_fail(aTHX_ f, failure)
1612 13           static SV *MY_future_fail(pTHX_ SV *f, SV *failure)
1613             {
1614 13           dSP;
1615              
1616 13           ENTER_with_name("future_fail");
1617 13           SAVETMPS;
1618              
1619             const char *method;
1620              
1621 13 50         PUSHMARK(SP);
1622 13 100         if(f) {
1623             assert(SvROK(f));
1624 10           PUSHs(f);
1625             method = "AWAIT_FAIL";
1626             }
1627             else {
1628 3           PUSHs(future_classname());
1629             method = "AWAIT_NEW_FAIL";
1630             }
1631 13           mPUSHs(newSVsv(failure));
1632 13           PUTBACK;
1633              
1634 13           call_method(method, G_SCALAR);
1635              
1636 13           SPAGAIN;
1637              
1638 13           SV *ret = SvREFCNT_inc(POPs);
1639              
1640 13 50         FREETMPS;
1641 13           LEAVE_with_name("future_fail");
1642              
1643 13           return ret;
1644             }
1645              
1646             #define future_new_from_proto(proto) MY_future_new_from_proto(aTHX_ proto)
1647 94           static SV *MY_future_new_from_proto(pTHX_ SV *proto)
1648             {
1649             assert(SvROK(proto));
1650              
1651 94           dSP;
1652              
1653 94           ENTER_with_name("future_new_from_proto");
1654 94           SAVETMPS;
1655              
1656 94 50         PUSHMARK(SP);
1657 94           PUSHs(proto);
1658 94           PUTBACK;
1659              
1660 94           call_method("AWAIT_CLONE", G_SCALAR);
1661              
1662 94           SPAGAIN;
1663              
1664 94           SV *f = SvREFCNT_inc(POPs);
1665              
1666 94 50         FREETMPS;
1667 94           LEAVE_with_name("future_new_from_proto");
1668              
1669 94 50         if(!SvROK(f))
1670 0           croak("Expected Future->new to yield a new reference");
1671              
1672             assert(SvREFCNT(f) == 1);
1673             assert(SvREFCNT(SvRV(f)) == 1);
1674 94           return f;
1675             }
1676              
1677             #define future_is_ready(f) MY_future_check(aTHX_ f, "AWAIT_IS_READY")
1678             #define future_is_cancelled(f) MY_future_check(aTHX_ f, "AWAIT_IS_CANCELLED")
1679 376           static bool MY_future_check(pTHX_ SV *f, const char *method)
1680             {
1681 376           dSP;
1682              
1683 376 50         if(!f || !SvOK(f))
    50          
    0          
    0          
1684 0           panic("ARGH future_check() on undefined value\n");
1685 376 50         if(!SvROK(f))
1686 0           panic("ARGH future_check() on non-reference\n");
1687              
1688 376           ENTER_with_name("future_check");
1689 376           SAVETMPS;
1690              
1691 376 50         PUSHMARK(SP);
1692 376 50         EXTEND(SP, 1);
1693 376           PUSHs(f);
1694 376           PUTBACK;
1695              
1696 376           call_method(method, G_SCALAR);
1697              
1698 376           SPAGAIN;
1699              
1700 376 50         bool ret = SvTRUEx(POPs);
    50          
    0          
    100          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    100          
    0          
1701              
1702 376           PUTBACK;
1703 376 50         FREETMPS;
1704 376           LEAVE_with_name("future_check");
1705              
1706 376           return ret;
1707             }
1708              
1709             #define future_get_to_stack(f, gimme) MY_future_get_to_stack(aTHX_ f, gimme)
1710 117           static void MY_future_get_to_stack(pTHX_ SV *f, I32 gimme)
1711             {
1712 117           dSP;
1713              
1714 117           ENTER_with_name("future_get_to_stack");
1715              
1716 117 50         PUSHMARK(SP);
1717 117 50         EXTEND(SP, 1);
1718 117           PUSHs(f);
1719 117           PUTBACK;
1720              
1721 117           call_method("AWAIT_GET", gimme);
1722              
1723 108           LEAVE_with_name("future_get_to_stack");
1724 108           }
1725              
1726             #define future_on_ready(f, code) MY_future_on_ready(aTHX_ f, code)
1727 119           static void MY_future_on_ready(pTHX_ SV *f, CV *code)
1728             {
1729 119           dSP;
1730              
1731 119           ENTER_with_name("future_on_ready");
1732 119           SAVETMPS;
1733              
1734 119 50         PUSHMARK(SP);
1735 119 50         EXTEND(SP, 2);
1736 119           PUSHs(f);
1737 119           mPUSHs(newRV_inc((SV *)code));
1738 119           PUTBACK;
1739              
1740 119           call_method("AWAIT_ON_READY", G_VOID);
1741              
1742 119 50         FREETMPS;
1743 119           LEAVE_with_name("future_on_ready");
1744 119           }
1745              
1746             #define future_on_cancel(f, code) MY_future_on_cancel(aTHX_ f, code)
1747 4           static void MY_future_on_cancel(pTHX_ SV *f, SV *code)
1748             {
1749 4           dSP;
1750              
1751 4           ENTER_with_name("future_on_cancel");
1752 4           SAVETMPS;
1753              
1754 4 50         PUSHMARK(SP);
1755 4 50         EXTEND(SP, 2);
1756 4           PUSHs(f);
1757 4           mPUSHs(code);
1758 4           PUTBACK;
1759              
1760 4           call_method("AWAIT_ON_CANCEL", G_VOID);
1761              
1762 4 50         FREETMPS;
1763 4           LEAVE_with_name("future_on_cancel");
1764 4           }
1765              
1766             #define future_chain_on_cancel(f1, f2) MY_future_chain_on_cancel(aTHX_ f1, f2)
1767 119           static void MY_future_chain_on_cancel(pTHX_ SV *f1, SV *f2)
1768             {
1769 119           dSP;
1770              
1771 119           ENTER_with_name("future_chain_on_cancel");
1772 119           SAVETMPS;
1773              
1774 119 50         PUSHMARK(SP);
1775 119 50         EXTEND(SP, 2);
1776 119           PUSHs(f1);
1777 119           PUSHs(f2);
1778 119           PUTBACK;
1779              
1780 119           call_method("AWAIT_CHAIN_CANCEL", G_VOID);
1781              
1782 119 50         FREETMPS;
1783 119           LEAVE_with_name("future_chain_on_cancel");
1784 119           }
1785              
1786             #define future_await_toplevel(f) MY_future_await_toplevel(aTHX_ f)
1787 5           static void MY_future_await_toplevel(pTHX_ SV *f)
1788             {
1789 5           dSP;
1790              
1791 5           ENTER_with_name("future_await_toplevel");
1792              
1793 5 50         PUSHMARK(SP);
1794 5 50         EXTEND(SP, 1);
1795 5           PUSHs(f);
1796 5           PUTBACK;
1797              
1798 5 100         call_method("AWAIT_WAIT", GIMME_V);
1799              
1800 4           LEAVE_with_name("future_await_toplevel");
1801 4           }
1802              
1803             /*
1804             * API functions
1805             */
1806              
1807 0           static HV *get_modhookdata(pTHX_ CV *cv, U32 flags, PADOFFSET precreate_padix)
1808             {
1809 0           SuspendedState *state = suspendedstate_get(cv);
1810              
1811 0 0         if(!state) {
1812 0 0         if(!precreate_padix)
1813             return NULL;
1814              
1815 0 0         if(!(flags & FAA_MODHOOK_CREATE))
1816             return NULL;
1817              
1818 0           return (HV *)PAD_SVl(precreate_padix + PRECREATE_MODHOOKDATA);
1819             }
1820              
1821 0 0         if((flags & FAA_MODHOOK_CREATE) && !state->modhookdata)
    0          
1822 0           state->modhookdata = newHV();
1823              
1824 0           return state->modhookdata;
1825             }
1826              
1827             /*
1828             * Custom ops
1829             */
1830              
1831             static XOP xop_enterasync;
1832 4           static OP *pp_enterasync(pTHX)
1833             {
1834 4           PADOFFSET precreate_padix = PL_op->op_targ;
1835              
1836 4 50         if(precreate_padix) {
1837 4           save_clearsv(&PAD_SVl(precreate_padix + PRECREATE_CANCEL));
1838 4           save_clearsv(&PAD_SVl(precreate_padix + PRECREATE_MODHOOKDATA));
1839             }
1840              
1841 4           return PL_op->op_next;
1842             }
1843              
1844             static XOP xop_leaveasync;
1845 106           static OP *pp_leaveasync(pTHX)
1846             {
1847 106           dSP;
1848 212           dMARK;
1849              
1850             SV *f = NULL;
1851             SV *ret = NULL;
1852              
1853 106           SuspendedState *state = suspendedstate_get(find_runcv(0));
1854 106 100         if(state && state->returning_future) {
    50          
1855             f = state->returning_future;
1856 83           state->returning_future = NULL;
1857             }
1858              
1859 106 100         if(f && !SvROK(f)) {
    100          
1860             /* async sub was abandoned. We just have to tidy up a bit and finish */
1861              
1862 2 50         if(SvTRUE(ERRSV)) {
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
1863             /* This error will otherwise go unreported; best we can do is warn() it */
1864 1           CV *curcv = find_runcv(0);
1865             GV *gv = CvGV(curcv);
1866 1 50         if(!CvANON(curcv))
1867 3 50         warn("Abandoned async sub %s::%s failed: %" SVf,
    50          
1868 3 50         HvNAME(GvSTASH(gv)), GvNAME(gv), SVfARG(ERRSV));
    50          
    0          
    50          
    50          
1869             else
1870 0 0         warn("Abandoned async sub CODE(0x%p) in package %s failed: %" SVf,
    0          
1871 0 0         curcv, HvNAME(GvSTASH(gv)), SVfARG(ERRSV));
    0          
    0          
    0          
    0          
1872             }
1873              
1874             goto abort;
1875             }
1876              
1877 104 50         if(SvTRUE(ERRSV)) {
    50          
    50          
    50          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
    50          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
1878 13 50         ret = future_fail(f, ERRSV);
1879             }
1880             else {
1881 91           ret = future_done_from_stack(f, mark);
1882             }
1883              
1884 104           SPAGAIN;
1885              
1886             abort: ; /* statement to keep C compilers happy */
1887 106           PERL_CONTEXT *cx = CX_CUR();
1888              
1889 106           SV **oldsp = PL_stack_base + cx->blk_oldsp;
1890              
1891             /* Pop extraneous stack items */
1892 210 100         while(SP > oldsp)
1893 104           POPs;
1894              
1895 106 100         if(ret) {
1896 104 50         EXTEND(SP, 1);
1897 104           mPUSHs(ret);
1898 104           PUTBACK;
1899             }
1900              
1901 106 100         if(f)
1902             SvREFCNT_dec(f);
1903              
1904 106           return PL_op->op_next;
1905             }
1906              
1907             static XOP xop_await;
1908 260           static OP *pp_await(pTHX)
1909             {
1910             /* We arrive here in either of two cases:
1911             * 1) Normal code flow has executed an 'await F' expression
1912             * 2) A previous await operation is resuming
1913             * Distinguish which by inspecting the state (if any) of the suspended context
1914             * magic on the containing CV
1915             */
1916 260           dSP;
1917             SV *f;
1918              
1919 260           CV *curcv = find_runcv(0);
1920             CV *origcv = curcv;
1921             bool defer_mortal_curcv = FALSE;
1922              
1923 260           PADOFFSET precreate_padix = PL_op->op_targ;
1924             /* Must fetch precancel AV now, before any pad fiddling or cv copy */
1925 260 100         AV *precancel = precreate_padix ? (AV *)PAD_SVl(precreate_padix + PRECREATE_CANCEL) : NULL;
1926              
1927 260           SuspendedState *state = suspendedstate_get(curcv);
1928              
1929 260 100         if(state && state->awaiting_future && CATCH_GET) {
    100          
    100          
1930             /* If we don't do this we get all the mess that is
1931             * https://rt.cpan.org/Ticket/Display.html?id=126037
1932             */
1933 11           return docatch(pp_await);
1934             }
1935              
1936 249           struct HookRegistrations *regs = registrations(FALSE);
1937              
1938 249 100         if(state && state->curcop)
    100          
1939 115           PL_curcop = state->curcop;
1940              
1941             TRACEPRINT("ENTER await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop));
1942             if(state)
1943             TRACEPRINT(" (state=%p/{awaiting_future=%p, returning_future=%p})\n",
1944             state, state->awaiting_future, state->returning_future);
1945             else
1946             TRACEPRINT(" (no state)\n");
1947              
1948 249 100         if(state) {
1949 143 100         if(!SvROK(state->returning_future) || future_is_cancelled(state->returning_future)) {
    100          
1950 8 100         if(!SvROK(state->returning_future)) {
1951             GV *gv = CvGV(curcv);
1952 3 100         if(!CvANON(curcv))
1953 2 50         warn("Suspended async sub %s::%s lost its returning future", HvNAME(GvSTASH(gv)), GvNAME(gv));
    50          
    50          
    0          
    50          
    50          
1954             else
1955 1 50         warn("Suspended async sub CODE(0x%p) in package %s lost its returning future", curcv, HvNAME(GvSTASH(gv)));
    50          
    50          
    0          
    50          
    50          
1956             }
1957              
1958             TRACEPRINT(" CANCELLED\n");
1959              
1960 8           suspendedstate_cancel(state);
1961              
1962 8 50         PUSHMARK(SP);
1963 8           PUTBACK;
1964 8           return PL_ppaddr[OP_RETURN](aTHX);
1965             }
1966             }
1967              
1968 241 100         if(state && state->awaiting_future) {
    100          
1969             I32 orig_height;
1970              
1971             TRACEPRINT(" RESUME\n");
1972              
1973             f = state->awaiting_future;
1974 108           sv_2mortal(state->awaiting_future);
1975 108           state->awaiting_future = NULL;
1976              
1977             /* Before we restore the stack we first need to POP the caller's
1978             * arguments, as we don't care about those
1979             */
1980 108           orig_height = CX_CUR()->blk_oldsp;
1981 216 100         while(sp > PL_stack_base + orig_height)
1982 108           POPs;
1983 108           PUTBACK;
1984              
1985             /* We also need to clean up the markstack and insert a new mark at the
1986             * beginning
1987             */
1988 108           orig_height = CX_CUR()->blk_oldmarksp;
1989 108 50         while(PL_markstack_ptr > PL_markstack + orig_height)
1990             POPMARK;
1991 108 50         PUSHMARK(SP);
1992              
1993             /* Legacy ones first */
1994             {
1995 108           SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", FALSE);
1996 108 50         if(hookp && SvOK(*hookp) && SvUV(*hookp)) {
    0          
    0          
    0          
    0          
    0          
1997 0           warn("Invoking legacy Future::AsyncAwait suspendhook for PRERESUME phase");
1998 0 0         SuspendHookFunc *hook = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
1999 0 0         if(!state->modhookdata)
2000 0           state->modhookdata = newHV();
2001              
2002 0           (*hook)(aTHX_ FAA_PHASE_PRERESUME, curcv, state->modhookdata);
2003             }
2004             }
2005              
2006             /* New ones after */
2007 108 50         if(regs)
2008 0 0         RUN_HOOKS_REV(pre_resume, curcv, state->modhookdata);
    0          
2009              
2010 108           suspendedstate_resume(state, curcv);
2011              
2012 108 50         if(regs)
2013 0 0         RUN_HOOKS_FWD(post_resume, curcv, state->modhookdata);
    0          
2014              
2015             #ifdef DEBUG_SHOW_STACKS
2016             debug_showstack("Stack after resume");
2017             #endif
2018             }
2019             else {
2020 133           f = POPs;
2021 133           PUTBACK;
2022             }
2023              
2024 241 50         if(!sv_isobject(f))
2025 0           croak("Expected a blessed object reference to await");
2026              
2027 241 100         if(PL_op->op_flags & OPf_SPECIAL) {
2028 5           future_await_toplevel(f);
2029 4           return PL_op->op_next;
2030             }
2031              
2032 236 100         if(future_is_ready(f)) {
2033             assert(CvDEPTH(curcv) > 0);
2034             TRACEPRINT(" READY\n");
2035 117 100         if(state)
2036 110           state->curcop = NULL;
2037             /* This might throw */
2038 117 100         future_get_to_stack(f, GIMME_V);
2039             TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop));
2040 108           return PL_op->op_next;
2041             }
2042              
2043             #ifdef DEBUG_SHOW_STACKS
2044             debug_showstack("Stack before suspend");
2045             #endif
2046              
2047 119 100         if(!state) {
2048             /* Clone the CV and then attach suspendedstate magic to it */
2049              
2050             /* No point copying a normal lexical slot because the suspend logic is
2051             * about to capture all the pad slots from the running CV (orig) and
2052             * they'll be restored into this new one later by resume.
2053             */
2054             CV *runcv = curcv;
2055 94           curcv = cv_copy_flags(runcv, CV_COPY_NULL_LEXICALS);
2056 94           state = suspendedstate_new(curcv);
2057              
2058 94 100         HV *premodhookdata = precreate_padix ? (HV *)PAD_SVl(precreate_padix + PRECREATE_MODHOOKDATA) : NULL;
2059 94 100         if(premodhookdata) {
2060 3           state->modhookdata = premodhookdata;
2061 3           PAD_SVl(precreate_padix + PRECREATE_MODHOOKDATA) = NULL; /* steal it */
2062             }
2063              
2064 94 50         if(regs) {
2065 0 0         if(!state->modhookdata)
2066 0           state->modhookdata = newHV();
2067 0 0         RUN_HOOKS_FWD(post_cv_copy, runcv, curcv, state->modhookdata);
    0          
2068             }
2069              
2070             TRACEPRINT(" SUSPEND cloned CV->%p\n", curcv);
2071             defer_mortal_curcv = TRUE;
2072             }
2073             else {
2074             TRACEPRINT(" SUSPEND reuse CV\n");
2075             }
2076              
2077 119           state->curcop = PL_curcop;
2078              
2079 119 50         if(regs)
2080 0 0         RUN_HOOKS_FWD(pre_suspend, curcv, state->modhookdata);
    0          
2081              
2082 119           suspendedstate_suspend(state, origcv);
2083              
2084             /* New ones first */
2085 119 50         if(regs)
2086 0 0         RUN_HOOKS_FWD(post_suspend, curcv, state->modhookdata);
    0          
2087              
2088             /* Legacy ones after */
2089             {
2090 119           SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", FALSE);
2091 119 50         if(hookp && SvOK(*hookp) && SvUV(*hookp)) {
    0          
    0          
    0          
    0          
    0          
2092 0           warn("Invoking legacy Future::AsyncAwait suspendhook for POSTSUSPEND phase");
2093 0 0         SuspendHookFunc *hook = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
2094 0 0         if(!state->modhookdata)
2095 0           state->modhookdata = newHV();
2096              
2097 0           (*hook)(aTHX_ FAA_PHASE_POSTSUSPEND, curcv, state->modhookdata);
2098             }
2099             }
2100              
2101 119           CvSTART(curcv) = PL_op; /* resume from here */
2102 119           future_on_ready(f, curcv);
2103              
2104             /* If the Future implementation's ->AWAIT_ON_READY failed to capture this CV
2105             * then we'll segfault later after SvREFCNT_dec() on it. We can at least
2106             * detect that here
2107             */
2108 119 50         if(SvREFCNT(curcv) < 2) {
2109 0           croak("AWAIT_ON_READY failed to capture the CV");
2110             }
2111              
2112 119           state->awaiting_future = newSVsv(f);
2113 119           sv_rvweaken(state->awaiting_future);
2114              
2115 119 100         if(!state->returning_future) {
2116 94           state->returning_future = future_new_from_proto(f);
2117              
2118 94 100         if(precancel) {
2119             I32 i;
2120 6 50         for(i = 0; i < av_count(precancel); i++)
    100          
2121 3           future_on_cancel(state->returning_future, AvARRAY(precancel)[i]);
2122 3           AvFILLp(precancel) = -1;
2123             }
2124             #ifndef HAVE_FUTURE_CHAIN_CANCEL
2125             /* We can't chain the cancellation but we do need a different way to
2126             * invoke the defer and finally blocks
2127             */
2128             future_on_cancel(state->returning_future, newRV_inc((SV *)curcv));
2129             #endif
2130             }
2131              
2132 119 100         if(defer_mortal_curcv)
2133             SvREFCNT_dec((SV *)curcv);
2134              
2135 119 50         PUSHMARK(SP);
2136 119           mPUSHs(newSVsv(state->returning_future));
2137 119           PUTBACK;
2138              
2139 119 100         if(!SvWEAKREF(state->returning_future))
2140 94           sv_rvweaken(state->returning_future);
2141 119 50         if(!SvROK(state->returning_future))
2142 0           panic("ARGH we lost state->returning_future for curcv=%p\n", curcv);
2143              
2144             #ifdef HAVE_FUTURE_CHAIN_CANCEL
2145 119           future_chain_on_cancel(state->returning_future, state->awaiting_future);
2146              
2147 119 50         if(!SvROK(state->returning_future))
2148 0           panic("ARGH we lost state->returning_future for curcv=%p\n", curcv);
2149             #endif
2150              
2151 119 50         if(!SvROK(state->awaiting_future))
2152 0           panic("ARGH we lost state->awaiting_future for curcv=%p\n", curcv);
2153              
2154             TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop));
2155              
2156 119           return PL_ppaddr[OP_RETURN](aTHX);
2157             }
2158              
2159             static XOP xop_pushcancel;
2160 4           static OP *pp_pushcancel(pTHX)
2161             {
2162 4           SuspendedState *state = suspendedstate_get(find_runcv(0));
2163              
2164 4           CV *on_cancel = cv_clone((CV *)cSVOP->op_sv);
2165              
2166 4 100         if(state && state->returning_future) {
    50          
2167 1           future_on_cancel(state->returning_future, newRV_noinc((SV *)on_cancel));
2168             }
2169             else {
2170 3           PADOFFSET precreate_padix = PL_op->op_targ;
2171 3           AV *precancel = (AV *)PAD_SVl(precreate_padix + PRECREATE_CANCEL);
2172 3           av_push(precancel, newRV_noinc((SV *)on_cancel));
2173             }
2174              
2175 4           return PL_op->op_next;
2176             }
2177              
2178             enum {
2179             NO_FORBID,
2180             FORBID_FOREACH_NONLEXICAL,
2181             FORBID_MAP,
2182             FORBID_GREP,
2183             };
2184              
2185             static void check_optree(pTHX_ OP *op, int forbid, COP **last_cop);
2186 1868           static void check_optree(pTHX_ OP *op, int forbid, COP **last_cop)
2187             {
2188             OP *op_first;
2189             OP *kid = NULL;
2190              
2191 1868 100         if(OP_CLASS(op) == OA_COP)
    100          
2192 304           *last_cop = (COP *)op;
2193              
2194 1868           switch(op->op_type) {
2195             case OP_LEAVELOOP:
2196 17 100         if((op_first = cUNOPx(op)->op_first)->op_type != OP_ENTERITER)
2197             break;
2198              
2199             /* This is a foreach loop of some kind. If it's not using a lexical
2200             * iterator variable, disallow await inside the body.
2201             * Check the first child, then apply forbid to the remainder of the body
2202             */
2203 13           check_optree(aTHX_ op_first, forbid, last_cop);
2204 13 50         kid = OpSIBLING(op_first);
2205              
2206 13 100         if(!op_first->op_targ)
2207             forbid = FORBID_FOREACH_NONLEXICAL;
2208             break;
2209              
2210             case OP_MAPSTART:
2211             case OP_GREPSTART:
2212             /* children are: PUSHMARK, BODY, ITEMS... */
2213 3 50         if((op_first = cUNOPx(op)->op_first)->op_type != OP_PUSHMARK)
2214             break;
2215              
2216 3 50         kid = OpSIBLING(op_first);
2217 3 100         check_optree(aTHX_ kid,
2218 3           op->op_type == OP_MAPSTART ? FORBID_MAP : FORBID_GREP, last_cop);
2219              
2220 1 50         kid = OpSIBLING(kid);
2221             break;
2222              
2223             case OP_CUSTOM:
2224 109 100         if(op->op_ppaddr != &pp_await)
2225             break;
2226 100 100         if(!forbid)
2227             /* await is allowed here */
2228             break;
2229              
2230             char *reason;
2231 3           switch(forbid) {
2232             case FORBID_FOREACH_NONLEXICAL:
2233             reason = "foreach on non-lexical iterator variable";
2234 1           break;
2235             case FORBID_MAP:
2236             reason = "map";
2237 1           break;
2238             case FORBID_GREP:
2239             reason = "grep";
2240 1           break;
2241             }
2242              
2243 3 50         croak("await is not allowed inside %s at %s line %d.\n",
2244 9           reason, CopFILE(*last_cop), CopLINE(*last_cop));
2245             break;
2246             }
2247              
2248 1863 100         if(op->op_flags & OPf_KIDS) {
2249 807 100         if(!kid)
2250 807           kid = cUNOPx(op)->op_first;
2251 2533 100         for(; kid; kid = OpSIBLING(kid))
    100          
2252 1741           check_optree(aTHX_ kid, forbid, last_cop);
2253             }
2254 1848           }
2255              
2256             /*
2257             * Keyword plugins
2258             */
2259              
2260 117           static void parse_post_blockstart(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
2261             {
2262             /* Save the identity of the currently-compiling sub so that
2263             * await_keyword_plugin() can check
2264             */
2265 117           hv_stores(GvHV(PL_hintgv), "Future::AsyncAwait/PL_compcv", newSVuv(PTR2UV(PL_compcv)));
2266              
2267 117           hv_stores(GvHV(PL_hintgv), "Future::AsyncAwait/*precreate_padix", newRV_noinc(newSVuv(0)));
2268 117           }
2269              
2270 111           static void parse_pre_blockend(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
2271             {
2272             /* body might be NULL if an error happened; we check that below so for now
2273             * just be defensive
2274             */
2275 111 50         if(ctx->body) {
2276 111           COP *last_cop = PL_curcop;
2277 111           check_optree(aTHX_ ctx->body, NO_FORBID, &last_cop);
2278             }
2279              
2280             #ifdef HAVE_OP_ARGCHECK
2281             /* If the sub body is using signatures, we want to pull the OP_ARGCHECK
2282             * outside the try block. This has two advantages:
2283             * 1. arity checks appear synchronous from the perspective of the caller;
2284             * immediate exceptions rather than failed Futures
2285             * 2. it makes Syntax::Keyword::MultiSub able to handle `async multi sub`
2286             */
2287             OP *argcheckop = NULL;
2288 108 100         if(ctx->body->op_type == OP_LINESEQ) {
2289             OP *lineseq = ctx->body;
2290 105           OP *o = cLISTOPx(lineseq)->op_first;
2291             /* OP_ARGCHECK is often found inside a second inner nested OP_LINESEQ that
2292             * was op_null'ed out
2293             */
2294 105 50         if(o->op_type == OP_NULL && o->op_flags & OPf_KIDS &&
    0          
    0          
2295 0           cUNOPo->op_first->op_type == OP_LINESEQ) {
2296             lineseq = cUNOPo->op_first;
2297 0           o = cLISTOPx(lineseq)->op_first;
2298             }
2299 210 50         if(o->op_type == OP_NEXTSTATE &&
    100          
2300 105 50         OpSIBLING(o)->op_type == OP_ARGCHECK) {
2301             /* Splice out the NEXTSTATE+ARGCHECK ops */
2302             argcheckop = o; /* technically actually the NEXTSTATE before it */
2303              
2304 7 50         o = OpSIBLING(OpSIBLING(o));
    50          
    50          
2305 7 50         OpMORESIB_set(OpSIBLING(argcheckop), NULL);
    50          
2306              
2307 7           cLISTOPx(lineseq)->op_first = o;
2308             }
2309             }
2310             #endif
2311              
2312             /* turn block into
2313             * NEXTSTATE; PUSHMARK; eval { BLOCK }; LEAVEASYNC
2314             */
2315              
2316 108           OP *body = newSTATEOP(0, NULL, NULL);
2317              
2318 108           PADOFFSET precreate_padix = get_precreate_padix();
2319 108 100         if(precreate_padix) {
2320             OP *enterasync;
2321 4           body = op_append_elem(OP_LINESEQ, body,
2322             enterasync = newOP_CUSTOM(&pp_enterasync, 0));
2323              
2324 4           enterasync->op_targ = precreate_padix;
2325             }
2326              
2327 108           body = op_append_elem(OP_LINESEQ, body, newOP(OP_PUSHMARK, 0));
2328              
2329             OP *try;
2330 108           body = op_append_elem(OP_LINESEQ, body, try = newUNOP(OP_ENTERTRY, 0, ctx->body));
2331 108           op_contextualize(try, G_ARRAY);
2332              
2333 108           body = op_append_elem(OP_LINESEQ, body, newOP_CUSTOM(&pp_leaveasync, OPf_WANT_SCALAR));
2334              
2335             #ifdef HAVE_OP_ARGCHECK
2336 108 100         if(argcheckop) {
2337             assert(body->op_type == OP_LINESEQ);
2338             /* Splice the argcheckop back into the start of the lineseq */
2339             OP *o = argcheckop;
2340 14 50         while(OpSIBLING(o))
    100          
2341 14 50         o = OpSIBLING(o);
2342              
2343 7           OpMORESIB_set(o, cLISTOPx(body)->op_first);
2344 7           cLISTOPx(body)->op_first = argcheckop;
2345             }
2346             #endif
2347              
2348 108           ctx->body = body;
2349 108           }
2350              
2351 108           static void parse_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
2352             {
2353 108 100         if(CvLVALUE(ctx->cv))
2354 1           warn("Pointless use of :lvalue on async sub");
2355 108           }
2356              
2357             static struct XSParseSublikeHooks hooks_async = {
2358             .flags = XS_PARSE_SUBLIKE_FLAG_PREFIX,
2359              
2360             .post_blockstart = parse_post_blockstart,
2361             .pre_blockend = parse_pre_blockend,
2362             .post_newcv = parse_post_newcv,
2363             };
2364              
2365 110           static void check_await(pTHX_ void *hookdata)
2366             {
2367 110           SV **asynccvp = hv_fetchs(GvHV(PL_hintgv), "Future::AsyncAwait/PL_compcv", 0);
2368 110 100         if(asynccvp && SvUV(*asynccvp) == PTR2UV(PL_compcv))
    50          
    100          
2369             ; /* await inside regular `async sub` */
2370 8 100         else if(PL_compcv == PL_main_cv)
2371             ; /* toplevel await */
2372             else
2373 3 100         croak(CvEVAL(PL_compcv) ?
    50          
2374             "await is not allowed inside string eval" :
2375             "Cannot 'await' outside of an 'async sub'");
2376 107           }
2377              
2378 106           static int build_await(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
2379             {
2380 106           OP *expr = arg0->op;
2381              
2382 106 100         if(PL_compcv == PL_main_cv)
2383 5           *out = newUNOP_CUSTOM(&pp_await, OPf_SPECIAL, expr);
2384             else {
2385 101           *out = newUNOP_CUSTOM(&pp_await, 0, expr);
2386              
2387 101           (*out)->op_targ = get_precreate_padix();
2388             }
2389              
2390 106           return KEYWORD_PLUGIN_EXPR;
2391             }
2392              
2393             static struct XSParseKeywordHooks hooks_await = {
2394             .permit_hintkey = "Future::AsyncAwait/async",
2395             .check = &check_await,
2396             .piece1 = XPK_TERMEXPR_SCALARCTX,
2397             .build1 = &build_await,
2398             };
2399              
2400 4           static void check_cancel(pTHX_ void *hookdata)
2401             {
2402 4           SV **asynccvp = hv_fetchs(GvHV(PL_hintgv), "Future::AsyncAwait/PL_compcv", 0);
2403 4 50         if(!asynccvp || SvUV(*asynccvp) != PTR2UV(PL_compcv))
    50          
    50          
2404 0 0         croak(CvEVAL(PL_compcv) ?
    0          
2405             "CANCEL is not allowed inside string eval" :
2406             "Cannot 'CANCEL' outside of an 'async sub'");
2407              
2408             #ifdef WARN_EXPERIMENTAL
2409 4 50         if(!hv_fetchs(GvHV(PL_hintgv), "Future::AsyncAwait/experimental(cancel)", 0)) {
2410 0           Perl_ck_warner(aTHX_ packWARN(WARN_EXPERIMENTAL),
2411             "CANCEL block syntax is experimental and may be changed or removed without notice");
2412             }
2413             #endif
2414 4           }
2415              
2416 4           static int build_cancel(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
2417             {
2418 4           CV *on_cancel = arg0->cv;
2419             OP *pushcancel;
2420              
2421 4           *out = op_prepend_elem(OP_LINESEQ,
2422             (pushcancel = newSVOP_CUSTOM(&pp_pushcancel, 0, (SV *)on_cancel)), NULL);
2423              
2424 4           pushcancel->op_targ = get_or_create_precreate_padix();
2425              
2426 4           return KEYWORD_PLUGIN_STMT;
2427             }
2428              
2429             static struct XSParseKeywordHooks hooks_cancel = {
2430             .permit_hintkey = "Future::AsyncAwait/async",
2431             .check = &check_cancel,
2432             .piece1 = XPK_ANONSUB,
2433             .build1 = &build_cancel,
2434             };
2435              
2436             /*
2437             * Back-compat support
2438             */
2439              
2440             struct AsyncAwaitHookFuncs_v1
2441             {
2442             U32 flags;
2443             void (*post_cv_copy)(pTHX_ CV *runcv, CV *cv, HV *modhookdata, void *hookdata);
2444             /* no pre_suspend */
2445             void (*post_suspend)(pTHX_ CV *cv, HV *modhookdata, void *hookdata);
2446             void (*pre_resume) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
2447             /* no post_resume */
2448             void (*free) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
2449             };
2450              
2451 0           static void register_faa_hook_v1(pTHX_ const struct AsyncAwaitHookFuncs_v1 *hookfuncs_v1, void *hookdata)
2452             {
2453             /* No flags are recognised; complain if the caller requested any */
2454 0 0         if(hookfuncs_v1->flags)
2455 0           croak("Unrecognised hookfuncs->flags value %08x", hookfuncs_v1->flags);
2456              
2457             struct AsyncAwaitHookFuncs *hookfuncs;
2458 0           Newx(hookfuncs, 1, struct AsyncAwaitHookFuncs);
2459              
2460 0           hookfuncs->flags = 0;
2461 0           hookfuncs->post_cv_copy = hookfuncs_v1->post_cv_copy;
2462 0           hookfuncs->pre_suspend = NULL;
2463 0           hookfuncs->post_suspend = hookfuncs_v1->post_suspend;
2464 0           hookfuncs->pre_resume = hookfuncs_v1->pre_resume;
2465 0           hookfuncs->post_resume = NULL;
2466 0           hookfuncs->free = hookfuncs_v1->free;
2467              
2468 0           register_faa_hook(aTHX_ hookfuncs, hookdata);
2469 0           }
2470              
2471             MODULE = Future::AsyncAwait PACKAGE = Future::AsyncAwait
2472              
2473             int
2474             __cxstack_ix()
2475             CODE:
2476 20           RETVAL = cxstack_ix;
2477             OUTPUT:
2478             RETVAL
2479              
2480             BOOT:
2481 45           XopENTRY_set(&xop_enterasync, xop_name, "enterasync");
2482 45           XopENTRY_set(&xop_enterasync, xop_desc, "enterasync()");
2483 45           XopENTRY_set(&xop_enterasync, xop_class, OA_BASEOP);
2484 45           Perl_custom_op_register(aTHX_ &pp_enterasync, &xop_enterasync);
2485              
2486 45           XopENTRY_set(&xop_leaveasync, xop_name, "leaveasync");
2487 45           XopENTRY_set(&xop_leaveasync, xop_desc, "leaveasync()");
2488 45           XopENTRY_set(&xop_leaveasync, xop_class, OA_UNOP);
2489 45           Perl_custom_op_register(aTHX_ &pp_leaveasync, &xop_leaveasync);
2490              
2491 45           XopENTRY_set(&xop_await, xop_name, "await");
2492 45           XopENTRY_set(&xop_await, xop_desc, "await()");
2493 45           XopENTRY_set(&xop_await, xop_class, OA_UNOP);
2494 45           Perl_custom_op_register(aTHX_ &pp_await, &xop_await);
2495              
2496 45           XopENTRY_set(&xop_pushcancel, xop_name, "pushcancel");
2497 45           XopENTRY_set(&xop_pushcancel, xop_desc, "pushcancel()");
2498 45           XopENTRY_set(&xop_pushcancel, xop_class, OA_SVOP);
2499 45           Perl_custom_op_register(aTHX_ &pp_pushcancel, &xop_pushcancel);
2500              
2501 45           boot_xs_parse_keyword(0.13);
2502 45           boot_xs_parse_sublike(0.14);
2503              
2504             register_xs_parse_sublike("async", &hooks_async, NULL);
2505              
2506             register_xs_parse_keyword("await", &hooks_await, NULL);
2507             register_xs_parse_keyword("CANCEL", &hooks_cancel, NULL);
2508             #ifdef HAVE_DMD_HELPER
2509             DMD_SET_MAGIC_HELPER(&vtbl_suspendedstate, dumpmagic_suspendedstate);
2510             #endif
2511              
2512 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MIN", 1), 1);
2513 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MAX", 1), FUTURE_ASYNCAWAIT_ABI_VERSION);
2514              
2515 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/register()@2", 1),
2516             PTR2UV(®ister_faa_hook));
2517 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/register()@1", 1),
2518             PTR2UV(®ister_faa_hook_v1));
2519 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/get_modhookdata()@1", 1),
2520             PTR2UV(&get_modhookdata));
2521 45           sv_setiv(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/make_precreate_padix()@1", 1),
2522             PTR2UV(&S_get_or_create_precreate_padix));
2523              
2524             {
2525             AV *run_on_loaded = NULL;
2526             SV **svp;
2527 45 50         if(svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/on_loaded", FALSE)) {
2528 0           run_on_loaded = (AV *)SvREFCNT_inc(*svp);
2529 0           hv_deletes(PL_modglobal, "Future::AsyncAwait/on_loaded", 0);
2530             }
2531              
2532 45           hv_stores(PL_modglobal, "Future::AsyncAwait/loaded", &PL_sv_yes);
2533              
2534 45 50         if(run_on_loaded) {
2535 0           svp = AvARRAY(run_on_loaded);
2536              
2537             int i;
2538 0 0         for(i = 0; i < AvFILL(run_on_loaded); i += 2) {
    0          
2539 0 0         void (*func)(pTHX_ void *data) = INT2PTR(void *, SvUV(svp[i ]));
2540 0 0         void *data = INT2PTR(void *, SvUV(svp[i+1]));
2541              
2542 0           (*func)(aTHX_ data);
2543             }
2544              
2545             SvREFCNT_dec(run_on_loaded);
2546             }
2547             }