File Coverage

Gather.xs
Criterion Covered Total %
statement 116 117 99.1
branch 35 42 83.3
condition n/a
subroutine n/a
pod n/a
total 151 159 94.9


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "callchecker0.h"
4             #include "callparser.h"
5             #include "XSUB.h"
6             #include "ppport.h"
7              
8             #define SVt_PADNAME SVt_PVMG
9              
10             #ifndef COP_SEQ_RANGE_LOW_set
11             # ifdef newPADNAMEpvn
12             # define COP_SEQ_RANGE_LOW_set(sv,val) \
13             do { (sv)->xpadn_low = (val); } while (0)
14             # define COP_SEQ_RANGE_HIGH_set(sv,val) \
15             do { (sv)->xpadn_high = (val); } while (0)
16             # else
17             # define COP_SEQ_RANGE_LOW_set(sv,val) \
18             do { ((XPVNV *)SvANY(sv))->xnv_u.xpad_cop_seq.xlow = val; } while (0)
19             # define COP_SEQ_RANGE_HIGH_set(sv,val) \
20             do { ((XPVNV *)SvANY(sv))->xnv_u.xpad_cop_seq.xhigh = val; } while (0)
21             # endif
22             #endif
23              
24             #ifndef PERL_PADSEQ_INTRO
25             # define PERL_PADSEQ_INTRO I32_MAX
26             #endif /* !PERL_PADSEQ_INTRO */
27              
28             #ifndef pad_findmy_pvs
29             # define pad_findmy_pvs(n,f) pad_findmy((""n""), (sizeof(""n"") - 1), f)
30             #endif
31              
32             #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
33             #define PERL_DECIMAL_VERSION \
34             PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
35             #define PERL_VERSION_GE(r,v,s) \
36             (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
37              
38             #if PERL_VERSION_GE(5,13,0)
39             # define lex_stuff_sv_(sv, flags) lex_stuff_sv((sv), (flags))
40             # define lex_stuff_pvn_(pv, len, flags) lex_stuff_pvn((pv), (len), (flags))
41             #else /* <5.13.0 */
42             # define lex_stuff_fixup() \
43             SvCUR_set(PL_parser->linestr, \
44             PL_parser->bufend - SvPVX(PL_parser->linestr))
45             # define lex_stuff_sv_(sv, flags) \
46             (lex_stuff_sv((sv), (flags)), lex_stuff_fixup())
47             # define lex_stuff_pvn_(pv, len, flags) \
48             (lex_stuff_pvn((pv), (len), (flags)), lex_stuff_fixup())
49             #endif
50              
51             #define lex_stuff_pvs_(s, flags) \
52             lex_stuff_pvn_((""s""), sizeof(""s"")-1, (flags))
53              
54             #ifndef padnamelist_store
55             # define padnamelist_store av_store
56             #endif
57              
58             #define QPARSE_DIRECTLY PERL_VERSION_GE(5,13,8)
59              
60             static PADOFFSET
61 30           pad_add_my_array_pvn (pTHX_ const char *namepv, STRLEN namelen)
62             {
63             PADOFFSET offset;
64             #ifdef newPADNAMEpvn
65             PADNAME *namesv;
66             #else
67             SV *namesv;
68             #endif
69             SV *myvar;
70              
71 30           myvar = *av_fetch(PL_comppad, AvFILLp(PL_comppad) + 1, 1);
72 30           sv_upgrade(myvar, SVt_PVAV);
73 30           offset = AvFILLp(PL_comppad);
74 30           SvPADMY_on(myvar);
75              
76 30           PL_curpad = AvARRAY(PL_comppad);
77             #ifdef newPADNAMEpvn
78 30           namesv = newPADNAMEpvn(namepv, namelen);
79             #else
80             namesv = newSV_type(SVt_PADNAME);
81             sv_setpvn(namesv, namepv, namelen);
82             #endif
83              
84 30           COP_SEQ_RANGE_LOW_set(namesv, PL_cop_seqmax);
85 30           COP_SEQ_RANGE_HIGH_set(namesv, PERL_PADSEQ_INTRO);
86 30           PL_cop_seqmax++;
87              
88 30           padnamelist_store(PL_comppad_name, offset, namesv);
89             #if PERL_VERSION_GE(5,19,3)
90 30           PadnamelistMAXNAMED(PL_comppad_name) = offset;
91             #endif
92              
93 30           return offset;
94             }
95              
96             static void
97 25           finish_gathering (pTHX_ AV *gatherer)
98             {
99 25           SvREADONLY_on(gatherer);
100 25           }
101              
102             static OP *
103 25           pp_my_padav (pTHX)
104             {
105 25           dTARGET;
106 25           SvREADONLY_off(TARG);
107 25           SAVEDESTRUCTOR_X(finish_gathering, TARG);
108 25           return PL_ppaddr[OP_PADAV](aTHX);
109             }
110              
111             static PADOFFSET
112 73           pad_findgatherer (pTHX_ GV *namegv)
113             {
114 73           PADOFFSET offset = pad_findmy_pvs("@List::Gather::gatherer", 0);
115 73 100         if (offset == NOT_IN_PAD)
116 2           croak("illegal use of %s outside of gather", GvNAME(namegv));
117              
118 71           return offset;
119             }
120              
121             #define GENOP_GATHER_INTRO 0x1
122              
123             static OP *
124 71           mygenop_padav (pTHX_ U32 flags, GV *op_namegv)
125             {
126 71 100         OP *pvarop = newOP(OP_PADAV,
127             (flags & GENOP_GATHER_INTRO) ? (OPpLVAL_INTRO<<8) : 0);
128              
129 71 100         if (flags & GENOP_GATHER_INTRO) {
130 30           pvarop->op_targ = pad_add_my_array_pvn(aTHX_ STR_WITH_LEN("@List::Gather::gatherer"));
131 30           pvarop->op_ppaddr = pp_my_padav;
132 30           PL_hints |= HINT_BLOCK_SCOPE;
133 30           return pvarop;
134             }
135              
136 41           pvarop->op_targ = pad_findgatherer(aTHX_ op_namegv);
137 40           return pvarop;
138             }
139              
140             static OP *
141 91           pp_take (pTHX)
142             {
143 91           dSP;
144 91           dMARK;
145 91           dORIGMARK;
146 91           dTARGET;
147              
148 91 100         if (SvREADONLY(TARG))
149 2           croak("attempting to take after gathering already completed");
150              
151 195 100         while (MARK < SP)
152 106           av_push((AV *)TARG, newSVsv(*++MARK));
153              
154 89 100         if (GIMME != G_ARRAY) {
    100          
155 87           MARK = ORIGMARK;
156 87 50         *++MARK = SP > ORIGMARK ? *SP : &PL_sv_undef;
157 87           SP = MARK;
158             }
159              
160 89           RETURN;
161             }
162              
163             static OP *
164 31           gen_take_op (pTHX_ OP *listop, PADOFFSET gatherer_offset)
165             {
166             OP *takeop;
167              
168 31           NewOpSz(0, takeop, sizeof(LISTOP));
169 31           takeop->op_type = OP_SPLICE;
170 31           takeop->op_ppaddr = pp_take;
171 31           takeop->op_targ = gatherer_offset;
172 31           cUNOPx(takeop)->op_flags = OPf_KIDS;
173             #ifdef op_sibling_splice
174 31           cLISTOPx(takeop)->op_first = cLISTOPx(takeop)->op_last = NULL;
175 31           op_sibling_splice(takeop, NULL, 0, listop);
176             #else
177             cLISTOPx(takeop)->op_first = cLISTOPx(takeop)->op_last = listop;
178             #endif
179              
180 31           return takeop;
181             }
182              
183             #if !QPARSE_DIRECTLY
184             SV *methodwrapper_sv;
185              
186             static OP *(*methodwrapper_nxck_entersub)(pTHX_ OP *o);
187              
188             static OP *
189             methodwrapper_myck_entersub (pTHX_ OP *entersubop)
190             {
191             OP *parent = entersubop;
192             OP *pushop, *sigop, *realop, *methop;
193              
194             pushop = cUNOPx(entersubop)->op_first;
195             if(!OpHAS_SIBLING(pushop)) {
196             parent = pushop;
197             pushop = cUNOPx(pushop)->op_first;
198             }
199              
200             if( (sigop = OpSIBLING(pushop)) && sigop->op_type == OP_CONST &&
201             cSVOPx_sv(sigop) == methodwrapper_sv &&
202             (realop = OpSIBLING(sigop)) &&
203             (methop = OpSIBLING(realop)) &&
204             !OpHAS_SIBLING(methop) &&
205             methop->op_type == OP_METHOD_NAMED)
206             {
207             #ifdef op_sibling_splice
208             op_sibling_splice(parent, sigop, 1, NULL);
209             #else
210             sigop->op_sibling = realop->op_sibling;
211             realop->op_sibling = NULL;
212             #endif
213             op_free(entersubop);
214             return realop;
215             }
216              
217             return methodwrapper_nxck_entersub(aTHX_ entersubop);
218             }
219              
220             static OP *
221             myck_entersub_gatherer_intro (pTHX_ OP *entersubop, GV *namegv, SV *protosv)
222             {
223             PERL_UNUSED_ARG(protosv);
224             op_free(entersubop);
225             return mygenop_padav(aTHX_ GENOP_GATHER_INTRO, namegv);
226             }
227             #endif
228              
229             static OP *
230 29           myck_entersub_gather (pTHX_ OP *entersubop, GV *namegv, SV *protosv)
231             {
232             OP *rv2cvop, *pushop, *blkop, *parent;
233              
234             PERL_UNUSED_ARG(namegv);
235             PERL_UNUSED_ARG(protosv);
236              
237 29           pushop = cUNOPx((parent = entersubop))->op_first;
238 29 50         if (!OpHAS_SIBLING(pushop))
239 29           pushop = cUNOPx((parent = pushop))->op_first;
240              
241 29 50         blkop = OpSIBLING(pushop);
242              
243             #ifdef op_sibling_splice
244 29           op_sibling_splice(parent, pushop, 1, NULL);
245             #else
246             rv2cvop = blkop->op_sibling;
247             blkop->op_sibling = NULL;
248             pushop->op_sibling = rv2cvop;
249             #endif
250 29           op_free(entersubop);
251              
252 29           return blkop;
253             }
254              
255             static OP *
256 32           myck_entersub_take (pTHX_ OP *entersubop, GV *namegv, SV *protosv)
257             {
258             OP *listop, *lastop, *rv2cvop;
259             PADOFFSET gatherer_offset;
260              
261             PERL_UNUSED_ARG(protosv);
262              
263 32           gatherer_offset = pad_findgatherer(aTHX_ namegv);
264              
265 31           entersubop = ck_entersub_args_list(entersubop);
266 31           listop = cUNOPx(entersubop)->op_first;
267 31 50         if (!listop)
268 0           return entersubop;
269              
270 31           entersubop->op_flags &= ~OPf_KIDS;
271 31           cUNOPx(entersubop)->op_first = NULL;
272 31           op_free(entersubop);
273              
274 31           lastop = cLISTOPx(listop)->op_first;
275 70 50         while (OpSIBLING(lastop) != cLISTOPx(listop)->op_last)
    100          
276 39 50         lastop = OpSIBLING(lastop);
277 31 50         rv2cvop = OpSIBLING(lastop);
278              
279             #ifdef op_sibling_splice
280 31           op_sibling_splice(listop, lastop, -1, NULL);
281             #else
282             lastop->op_sibling = NULL;
283             cLISTOPx(listop)->op_last = lastop;
284             #endif
285 31           op_free(rv2cvop);
286              
287 31           return gen_take_op(aTHX_ listop, gatherer_offset);
288             }
289              
290             static OP *
291 11           myck_entersub_gathered (pTHX_ OP *entersubop, GV *namegv, SV *protosv)
292             {
293             PERL_UNUSED_ARG(protosv);
294 11           op_free(entersubop);
295 11           return mygenop_padav(aTHX_ 0, namegv);
296             }
297              
298             static OP *
299 32           myparse_args_gather (pTHX_ GV *namegv, SV *psobj, U32 *flagsp)
300             {
301             bool had_paren, is_modifier;
302             #if QPARSE_DIRECTLY
303             int blk_floor;
304             OP *blkop, *initop;
305             #else
306             PERL_UNUSED_ARG(namegv);
307             #endif
308              
309             PERL_UNUSED_ARG(psobj);
310              
311 32           lex_read_space(0);
312 32           had_paren = lex_peek_unichar(0) == '(';
313 32 100         if (had_paren) {
314 7           lex_read_unichar(0);
315 7           lex_read_space(0);
316             }
317              
318 32           is_modifier = lex_peek_unichar(0) != '{';
319              
320 32 100         if (is_modifier && had_paren)
    100          
321 2           croak("syntax error");
322              
323             #if QPARSE_DIRECTLY
324 30           blk_floor = Perl_block_start(aTHX_ 1);
325 30           initop = mygenop_padav(aTHX_ GENOP_GATHER_INTRO, namegv);
326 30 100         blkop = op_prepend_elem(OP_LINESEQ, initop,
327             is_modifier ? parse_barestmt(0) : parse_block(0));
328 30           blkop = op_append_elem(OP_LINESEQ, blkop,
329             newSTATEOP(0, NULL, mygenop_padav(aTHX_ 0, namegv)));
330 30           blkop = Perl_block_end(aTHX_ blk_floor, blkop);
331              
332 30 100         if (had_paren) {
333 5           lex_read_space(0);
334 5 100         if (lex_peek_unichar(0) != ')')
335 1           croak("syntax error");
336 4           lex_read_unichar(0);
337 4           *flagsp |= CALLPARSER_PARENS;
338             }
339              
340 29           return op_scope(blkop);
341             #else
342             if (is_modifier)
343             croak("syntax error (statement modifier syntax not supported on perls before 5.13.8)");
344             lex_read_unichar(0);
345              
346             lex_stuff_pvs_("}}", 0);
347             lex_stuff_pvs_("List::Gather::_stuff(';List::Gather::gathered;}')", 0);
348             if (had_paren)
349             *flagsp |= CALLPARSER_PARENS;
350             else
351             lex_stuff_pvs_("List::Gather::_stuff(')');", 0);
352             lex_stuff_pvs_("BEGIN{B::Hooks::EndOfScope::on_scope_end{", 0);
353             lex_stuff_pvs_("->x(do{List::Gather::_gatherer_intro;do{", 0);
354              
355             return newSVOP(OP_CONST, 0, SvREFCNT_inc(methodwrapper_sv));
356             #endif
357             }
358              
359             MODULE = List::Gather PACKAGE = List::Gather
360              
361             void
362             gather (...)
363             CODE:
364             PERL_UNUSED_VAR(items);
365 1           croak("gather called as a function");
366              
367             void
368             take (...)
369             CODE:
370             PERL_UNUSED_VAR(items);
371 1           croak("take called as a function");
372              
373             void
374             gathered (...)
375             PROTOTYPE:
376             CODE:
377             PERL_UNUSED_VAR(items);
378 1           croak("gathered called as a function");
379              
380             #if !QPARSE_DIRECTLY
381              
382             void
383             _stuff(SV *sv)
384             PROTOTYPE: $
385             CODE:
386             lex_stuff_sv_(sv, 0);
387              
388             void
389             _gatherer_intro (...)
390             PROTOTYPE:
391             CODE:
392             PERL_UNUSED_VAR(items);
393             croak("_gatherer_intro called as a function");
394              
395             #endif
396              
397             bool
398             _QPARSE_DIRECTLY ()
399             CODE:
400 7           RETVAL = QPARSE_DIRECTLY;
401             OUTPUT:
402             RETVAL
403              
404             BOOT:
405             {
406             CV *gather_cv, *take_cv, *gathered_cv;
407             #if !QPARSE_DIRECTLY
408             CV *gatherer_intro_cv;
409              
410             methodwrapper_sv = newSVpvs("");
411             methodwrapper_nxck_entersub = PL_check[OP_ENTERSUB];
412             PL_check[OP_ENTERSUB] = methodwrapper_myck_entersub;
413              
414             gatherer_intro_cv = get_cv("List::Gather::_gatherer_intro", 0);
415             cv_set_call_checker(gatherer_intro_cv, myck_entersub_gatherer_intro,
416             (SV*)gatherer_intro_cv);
417             #endif
418              
419 7           gather_cv = get_cv("List::Gather::gather", 0);
420 7           take_cv = get_cv("List::Gather::take", 0);
421 7           gathered_cv = get_cv("List::Gather::gathered", 0);
422              
423 7           cv_set_call_parser(gather_cv, myparse_args_gather, &PL_sv_undef);
424              
425 7           cv_set_call_checker(gather_cv, myck_entersub_gather, (SV*)gather_cv);
426 7           cv_set_call_checker(take_cv, myck_entersub_take, (SV*)take_cv);
427 7           cv_set_call_checker(gathered_cv, myck_entersub_gathered, (SV*)gathered_cv);
428             }