1 /*    multicall.h		(version 1.0)
2  *
3  * Implements a poor-man's MULTICALL interface for old versions
4  * of perl that don't offer a proper one. Intended to be compatible
5  * with 5.6.0 and later.
6  *
7  */
8 
9 #ifdef dMULTICALL
10 #define REAL_MULTICALL
11 #else
12 #undef REAL_MULTICALL
13 
14 /* In versions of perl where MULTICALL is not defined (i.e. prior
15  * to 5.9.4), Perl_pad_push is not exported either. It also has
16  * an extra argument in older versions; certainly in the 5.8 series.
17  * So we redefine it here.
18  */
19 
20 #ifndef AVf_REIFY
21 #  ifdef SVpav_REIFY
22 #    define AVf_REIFY SVpav_REIFY
23 #  else
24 #    error Neither AVf_REIFY nor SVpav_REIFY is defined
25 #  endif
26 #endif
27 
28 #ifndef AvFLAGS
29 #  define AvFLAGS SvFLAGS
30 #endif
31 
32 static void
multicall_pad_push(pTHX_ AV * padlist,int depth)33 multicall_pad_push(pTHX_ AV *padlist, int depth)
34 {
35     if (depth <= AvFILLp(padlist))
36 	return;
37 
38     {
39 	SV** const svp = AvARRAY(padlist);
40 	AV* const newpad = newAV();
41 	SV** const oldpad = AvARRAY(svp[depth-1]);
42 	I32 ix = AvFILLp((AV*)svp[1]);
43         const I32 names_fill = AvFILLp((AV*)svp[0]);
44 	SV** const names = AvARRAY(svp[0]);
45 	AV *av;
46 
47 	for ( ;ix > 0; ix--) {
48 	    if (names_fill >= ix && names[ix] != &PL_sv_undef) {
49 		const char sigil = SvPVX(names[ix])[0];
50 		if ((SvFLAGS(names[ix]) & SVf_FAKE) || sigil == '&') {
51 		    /* outer lexical or anon code */
52 		    av_store(newpad, ix, SvREFCNT_inc(oldpad[ix]));
53 		}
54 		else {		/* our own lexical */
55 		    SV *sv;
56 		    if (sigil == '@')
57 			sv = (SV*)newAV();
58 		    else if (sigil == '%')
59 			sv = (SV*)newHV();
60 		    else
61 			sv = NEWSV(0, 0);
62 		    av_store(newpad, ix, sv);
63 		    SvPADMY_on(sv);
64 		}
65 	    }
66 	    else if (IS_PADGV(oldpad[ix]) || IS_PADCONST(oldpad[ix])) {
67 		av_store(newpad, ix, SvREFCNT_inc(oldpad[ix]));
68 	    }
69 	    else {
70 		/* save temporaries on recursion? */
71 		SV * const sv = NEWSV(0, 0);
72 		av_store(newpad, ix, sv);
73 		SvPADTMP_on(sv);
74 	    }
75 	}
76 	av = newAV();
77 	av_extend(av, 0);
78 	av_store(newpad, 0, (SV*)av);
79 	AvFLAGS(av) = AVf_REIFY;
80 
81 	av_store(padlist, depth, (SV*)newpad);
82 	AvFILLp(padlist) = depth;
83     }
84 }
85 
86 #define dMULTICALL \
87     SV **newsp;			/* set by POPBLOCK */			\
88     PERL_CONTEXT *cx;							\
89     CV *multicall_cv;							\
90     OP *multicall_cop;							\
91     bool multicall_oldcatch;						\
92     U8 hasargs = 0
93 
94 /* Between 5.9.1 and 5.9.2 the retstack was removed, and the
95    return op is now stored on the cxstack. */
96 #define HAS_RETSTACK (\
97   PERL_REVISION < 5 || \
98   (PERL_REVISION == 5 && PERL_VERSION < 9) || \
99   (PERL_REVISION == 5 && PERL_VERSION == 9 && PERL_SUBVERSION < 2) \
100 )
101 
102 
103 /* PUSHSUB is defined so differently on different versions of perl
104  * that it's easier to define our own version than code for all the
105  * different possibilities.
106  */
107 #if HAS_RETSTACK
108 #  define PUSHSUB_RETSTACK(cx)
109 #else
110 #  define PUSHSUB_RETSTACK(cx) cx->blk_sub.retop = Nullop;
111 #endif
112 #define MULTICALL_PUSHSUB(cx, the_cv) \
113         cx->blk_sub.cv = the_cv;					\
114         cx->blk_sub.olddepth = CvDEPTH(the_cv);				\
115         cx->blk_sub.hasargs = hasargs;					\
116         cx->blk_sub.lval = PL_op->op_private &				\
117                               (OPpLVAL_INTRO|OPpENTERSUB_INARGS);	\
118 	PUSHSUB_RETSTACK(cx)						\
119         if (!CvDEPTH(the_cv)) {						\
120             (void)SvREFCNT_inc(the_cv);					\
121             (void)SvREFCNT_inc(the_cv);					\
122             SAVEFREESV(the_cv);						\
123         }
124 
125 #define PUSH_MULTICALL(the_cv) \
126     STMT_START {							\
127 	CV *_nOnclAshIngNamE_ = the_cv;					\
128 	AV* padlist = CvPADLIST(_nOnclAshIngNamE_);			\
129 	multicall_cv = _nOnclAshIngNamE_;				\
130 	ENTER;								\
131  	multicall_oldcatch = CATCH_GET;					\
132 	SAVESPTR(CvROOT(multicall_cv)->op_ppaddr);			\
133 	CvROOT(multicall_cv)->op_ppaddr = PL_ppaddr[OP_NULL];		\
134 	SAVETMPS; SAVEVPTR(PL_op);					\
135 	CATCH_SET(TRUE);						\
136 	PUSHSTACKi(PERLSI_SORT);					\
137 	PUSHBLOCK(cx, CXt_SUB, PL_stack_sp);				\
138 	MULTICALL_PUSHSUB(cx, multicall_cv);				\
139 	if (++CvDEPTH(multicall_cv) >= 2) {				\
140 	    PERL_STACK_OVERFLOW_CHECK();				\
141 	    multicall_pad_push(aTHX_ padlist, CvDEPTH(multicall_cv));	\
142 	}								\
143 	SAVECOMPPAD();							\
144 	PL_comppad = (AV*) (AvARRAY(padlist)[CvDEPTH(multicall_cv)]);	\
145 	PL_curpad = AvARRAY(PL_comppad);				\
146 	multicall_cop = CvSTART(multicall_cv);				\
147     } STMT_END
148 
149 #define MULTICALL \
150     STMT_START {							\
151 	PL_op = multicall_cop;						\
152 	CALLRUNOPS(aTHX);						\
153     } STMT_END
154 
155 #define POP_MULTICALL \
156     STMT_START {							\
157 	CvDEPTH(multicall_cv)--;					\
158 	LEAVESUB(multicall_cv);						\
159 	POPBLOCK(cx,PL_curpm);						\
160 	POPSTACK;							\
161 	CATCH_SET(multicall_oldcatch);					\
162 	LEAVE;								\
163         SPAGAIN;                                                        \
164     } STMT_END
165 
166 #endif
167