File Coverage

Deep.xs
Criterion Covered Total %
statement 55 60 91.6
branch 28 46 60.8
condition n/a
subroutine n/a
pod n/a
total 83 106 78.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "ppport.h"
7              
8             #include "const-c.inc"
9              
10             #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
11             #define PERL_DECIMAL_VERSION PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
12             #define PERL_VERSION_GE(r,v,s) (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
13              
14             struct block_symbol_t {
15             CV * cv;
16             SV * symbol_SV;
17             };
18              
19             static Perl_ppaddr_t return_ppaddr;
20             static struct block_symbol_t * block_symbols;
21             static int block_symbols_capacity, block_symbols_n;
22              
23 5           static OP * my_pp_deep_ret(pTHX){
24 5           dSP; POPs;
25              
26 5 50         IV depth = SvIV(PL_stack_base[TOPMARK+1]);
27              
28 20 100         for(SV ** p = PL_stack_base+TOPMARK+1; p
29 15           *p = *(p+1);
30 5           POPs;
31              
32 5 100         if( depth <= 0 )
33 1           RETURN;
34              
35             OP * next_op;
36 14 100         while( depth-- )
37 10           next_op = return_ppaddr(aTHX);
38 4           RETURNOP(next_op);
39             }
40              
41 7           static OP * my_pp_sym_ret(pTHX){
42 7           dSP; POPs;
43              
44 7           SV * symbol_SV = PL_stack_base[TOPMARK+1];
45              
46 28 100         for(SV ** p = PL_stack_base+TOPMARK+1; p
47 21           *p = *(p+1);
48 7           POPs;
49              
50             while(true){
51 46 50         for(PERL_CONTEXT * cx = &cxstack[cxstack_ix]; cx>=cxstack; --cx){
52 46           switch( CxTYPE(cx) ){
53             default:
54 0           continue;
55             case CXt_SUB:
56 46 50         if( cx->cx_type & CXp_SUB_RE_FAKE )
57 0           continue;
58 159 100         for(struct block_symbol_t *p = block_symbols+block_symbols_n-1; p>=block_symbols; --p)
59 120 100         if( p->cv == cx->blk_sub.cv ){
60 20 100         if( !SvOK(p->symbol_SV) || sv_cmp(p->symbol_SV, symbol_SV)==0 )
    50          
    50          
    100          
61 7           RETURNOP(return_ppaddr(aTHX));
62             }
63             case CXt_EVAL:
64             case CXt_FORMAT:
65 39           goto DO_RETURN;
66             }
67             }
68             DO_RETURN:
69 39           return_ppaddr(aTHX);
70 39           }
71             }
72              
73 1           static OP * deep_ret_check(pTHX_ OP * o, GV * namegv, SV * ckobj){
74 1           o->op_ppaddr = my_pp_deep_ret;
75 1           return o;
76             }
77              
78 1           static OP * sym_ret_check(pTHX_ OP * o, GV * namegv, SV * ckobj){
79 1           o->op_ppaddr = my_pp_sym_ret;
80 1           return o;
81             }
82              
83 28           static int guard_free(pTHX_ SV * guard_SV, MAGIC * mg){
84 28 50         for(struct block_symbol_t * p=block_symbols+block_symbols_n-1; p>=block_symbols; --p)
85 28 50         if( (IV)p->cv == (IV)mg->mg_ptr ){
86 28           --block_symbols_n;
87 28           *p = block_symbols[block_symbols_n];
88 28           break;
89             }
90 28           return 0;
91             }
92              
93             static MGVTBL guard_vtbl = {
94             0, 0, 0, 0,
95             guard_free
96             };
97              
98             #if !PERL_VERSION_GE(5,14,0)
99             static CV* my_deep_ret_cv;
100             static CV* my_sym_ret_cv;
101             static OP* (*orig_entersub_check)(pTHX_ OP*);
102             static OP* my_entersub_check(pTHX_ OP* o){
103             CV *cv = NULL;
104             OP *cvop = OpSIBLING(((OpSIBLING(cUNOPo->op_first)) ? cUNOPo : ((UNOP*)cUNOPo->op_first))->op_first);
105             while( OpSIBLING(cvop) )
106             cvop = OpSIBLING(cvop);
107             if( cvop->op_type == OP_RV2CV && !(o->op_private & OPpENTERSUB_AMPER) ){
108             SVOP *tmpop = (SVOP*)((UNOP*)cvop)->op_first;
109             switch (tmpop->op_type) {
110             case OP_GV: {
111             GV *gv = cGVOPx_gv(tmpop);
112             cv = GvCVu(gv);
113             if (!cv)
114             tmpop->op_private |= OPpEARLY_CV;
115             } break;
116             case OP_CONST: {
117             SV *sv = cSVOPx_sv(tmpop);
118             if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
119             cv = (CV*)SvRV(sv);
120             } break;
121             }
122             if( cv==my_deep_ret_cv )
123             o->op_ppaddr = my_pp_deep_ret;
124             if( cv==my_sym_ret_cv )
125             o->op_ppaddr = my_pp_sym_ret;
126             }
127             return orig_entersub_check(aTHX_ o);
128             }
129             #endif
130              
131             MODULE = Return::Deep PACKAGE = Return::Deep
132              
133             INCLUDE: const-xs.inc
134              
135             void add_bound(SV * act_SV, SV * symbol_SV)
136             PPCODE:
137 28 50         if( !(SvOK(act_SV) && SvROK(act_SV) && SvTYPE(SvRV(act_SV))==SVt_PVCV) )
    0          
    0          
    50          
    50          
138 0           croak("there should be a code block");
139              
140 28           CV * act_CV = (CV*) SvRV(act_SV);
141 28           SV * guard_SV = newSV(0);
142              
143 28           sv_magicext(guard_SV, NULL, PERL_MAGIC_ext, &guard_vtbl, (char*) act_CV, 0);
144              
145 28 50         if( block_symbols_n >= block_symbols_capacity ){
146 0           block_symbols_capacity *= 2;
147 0 0         Renew(block_symbols, block_symbols_capacity, struct block_symbol_t);
148             }
149 28           block_symbols[block_symbols_n].cv = act_CV;
150 28           block_symbols[block_symbols_n].symbol_SV = symbol_SV;
151 28           ++block_symbols_n;
152              
153 28           PUSHs(sv_2mortal(newRV_noinc(guard_SV)));
154              
155             BOOT:
156 1           block_symbols_capacity = 8;
157 1           block_symbols_n = 0;
158 1 50         Newx(block_symbols, block_symbols_capacity, struct block_symbol_t);
159              
160 1           return_ppaddr = PL_ppaddr[OP_RETURN];
161             #if PERL_VERSION_GE(5,14,0)
162 1           cv_set_call_checker(get_cv("Return::Deep::deep_ret", TRUE), deep_ret_check, &PL_sv_undef);
163 1           cv_set_call_checker(get_cv("Return::Deep::sym_ret", TRUE), sym_ret_check, &PL_sv_undef);
164             #else
165             my_deep_ret_cv = get_cv("Return::Deep::deep_ret", TRUE);
166             my_sym_ret_cv = get_cv("Return::Deep::sym_ret", TRUE);
167             orig_entersub_check = PL_check[OP_ENTERSUB];
168             PL_check[OP_ENTERSUB] = my_entersub_check;
169             #endif