File Coverage

lib/Package/Prototype.xs
Criterion Covered Total %
statement 74 76 97.3
branch 48 66 72.7
condition n/a
subroutine n/a
pod n/a
total 122 142 85.9


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4              
5             #define PERL_NO_GET_CONTEXT /* we want efficiency */
6             #include
7             #include
8             #include
9              
10             #ifdef __cplusplus
11             } /* extern "C" */
12             #endif
13              
14             #define NEED_newSVpvn_flags
15             #include "ppport.h"
16              
17             #ifndef GvCV_set
18             # define GvCV_set(gv,cv) (GvGP(gv)->gp_cv = (cv))
19             #endif
20              
21             #ifndef gv_init_pvn
22             # define gv_init_pvn gv_init
23             #endif
24              
25             #define IsArrayRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVAV)
26             #define IsHashRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVHV)
27             #define IsCodeRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVCV)
28             #define WANT_ARRAY GIMME_V == G_ARRAY
29              
30             XS(XS_prototype_method);
31             XS(XS_prototype_getter);
32              
33             static GV *
34             prototype_gv_pvn(pTHX_ HV *stash, const char *name, STRLEN len, U32 flags)
35             {
36 159           GV *gv = (GV *)newSV(0);
37 159           gv_init_pvn(gv, stash, name, len, flags);
38             return gv;
39             }
40              
41             static GV *
42 129           prototype_gv_sv(pTHX_ HV *stash, SV *namesv)
43             {
44             U32 flag;
45             char *namepv;
46             STRLEN namelen;
47 129 50         namepv = SvPV(namesv, namelen);
48             if (SvUTF8(namesv)) flag = SVf_UTF8;
49 258           return prototype_gv_pvn(aTHX_ stash, namepv, namelen, flag);
50             }
51              
52             static void
53 129           add_method(pTHX_ HV *stash, SV *method, CV *code, char *key, I32 keylen)
54             {
55             GV *gv;
56 129           gv = prototype_gv_sv(aTHX_ stash, method);
57 129           GvCV_set(gv, code);
58 129           hv_store(stash, key, keylen, (SV *)gv, 0);
59 129           }
60              
61             static void
62 8           add_method_sv(pTHX_ HV *stash, SV *method, CV *code)
63             {
64             char *key;
65             STRLEN keylen;
66 8 50         key = SvPV(method, keylen);
67 8           add_method(aTHX_ stash, method, code, key, keylen);
68 8           }
69              
70             static CV *
71             make_closure(pTHX_ SV *retval)
72             {
73             CV *xsub;
74 110           xsub = newXS(NULL /* anonymous */, XS_prototype_getter, __FILE__);
75 110           CvXSUBANY(xsub).any_ptr = (void *)retval;
76             return xsub;
77             }
78              
79             static void
80 17           push_values(pTHX_ SV *retval)
81             {
82 17           dSP;
83 17 100         if (WANT_ARRAY && IsArrayRef(retval)) {
    100          
    50          
    100          
    100          
84             AV *av = (AV *)SvRV(retval);
85 1           I32 len = av_len(av) + 1;
86 1 50         EXTEND(SP, len);
    50          
87 11 100         for (I32 i = 0; i < len; i++){
88 10           SV **const svp = av_fetch(av, i, FALSE);
89 10 50         PUSHs(svp ? *svp : &PL_sv_undef);
90             }
91 16 100         } else if (WANT_ARRAY && IsHashRef(retval)) {
    100          
    50          
    100          
    50          
92             HV *hv = (HV *)SvRV(retval);
93             HE *he;
94 1           hv_iterinit(hv);
95 3 100         while ((he = hv_iternext(hv)) != NULL){
96 2 50         EXTEND(SP, 2);
97 2           PUSHs(hv_iterkeysv(he));
98 2           PUSHs(hv_iterval(hv, he));
99             }
100             } else {
101 15 50         XPUSHs(retval ? retval : &PL_sv_undef);
    50          
102             }
103 17           PUTBACK;
104 17           }
105              
106             static CV *
107             make_prototype_method(pTHX_ HV *stash)
108             {
109             CV *xsub;
110 30           xsub = newXS(NULL /* anonymous */, XS_prototype_method, __FILE__);
111 30           CvXSUBANY(xsub).any_ptr = (void *)stash;
112             return xsub;
113             }
114              
115             static void
116 30           install_prototype_method(pTHX_ HV *stash)
117             {
118             char *prototype = "prototype";
119             CV *prototype_cv = make_prototype_method(aTHX_ stash);
120             GV *prototype_glob = prototype_gv_pvn(aTHX_ stash, prototype, 9, 0);
121 30           GvCV_set(prototype_glob, prototype_cv);
122 30           hv_store(stash, prototype, 9, (SV *)prototype_glob, 0);
123 30           }
124              
125 17           XS(XS_prototype_getter)
126             {
127 34           dVAR; dXSARGS;
128 17           SV *retval = (SV *)CvXSUBANY(cv).any_ptr;
129 17           SP -= items; /* PPCODE */
130 17           PUTBACK;
131 17           push_values(aTHX_ retval);
132 17           }
133              
134 3           XS(XS_prototype_method)
135             {
136 6           dVAR; dXSARGS;
137 3 50         if ((items - 1) % 2 != 0)
138 0           Perl_croak(aTHX_ "Argument isn't hash type");
139            
140 3           HV *stash = (HV *)CvXSUBANY(cv).any_ptr;
141             I32 i = 1; /* First argument is skip: `my $self = shift;` */
142 11 100         while (i < items) {
143 8           SV *method = ST(i++);
144 8           SV *val = ST(i++);
145 11 100         CV *cv = IsCodeRef(val) ? (CV *)SvREFCNT_inc(SvRV(val)) : make_closure(aTHX_ val);
    50          
146 8           add_method_sv(aTHX_ stash, method, cv);
147             }
148 3           XSRETURN(0);
149             }
150              
151             MODULE = Package::Prototype PACKAGE = Package::Prototype
152             PROTOTYPES: DISABLE
153              
154             void *
155             bless(klass, ref, pkgsv=NULL)
156             SV *klass;
157             SV *ref;
158             SV *pkgsv;
159             PREINIT:
160             char *pkg;
161             STRLEN pkglen;
162             HE* entry;
163             HV *stash;
164             PPCODE:
165             {
166 30 50         if (!IsHashRef(ref))
    50          
167 0           Perl_croak(aTHX_ "Please pass an hash reference to the first argument");
168              
169 30 100         if (pkgsv) {
170 3 50         pkg = SvPV(pkgsv, pkglen);
171             } else {
172             pkg = "__ANON__";
173 27           pkglen = 8;
174             }
175              
176 30           stash = (HV *)sv_2mortal((SV *)newHV());
177 30           hv_name_set(stash, pkg, pkglen, 0);
178              
179 30           install_prototype_method(aTHX_ stash);
180              
181 30           HV *hv = (HV *)SvRV(ref);
182 30           hv_iterinit(hv);
183 151 100         while ((entry = hv_iternext(hv)) != NULL){
184             I32 keylen;
185 121           char* key = hv_iterkey(entry, &keylen);
186 121 50         if (0 < keylen && key[0] != '_') {
    50          
187 121           SV *method = hv_iterkeysv(entry);
188 121           SV *val = hv_delete(hv, key, keylen, 1);
189             SvREFCNT_inc(val); /* was made mortal by hv_delete */
190 121 100         CV *cv = IsCodeRef(val) ? (CV *)SvRV(val) : make_closure(aTHX_ val);
    100          
191 121           add_method(aTHX_ stash, method, cv, key, keylen);
192             }
193             }
194              
195 30           ST(0) = sv_bless(ref, stash);
196 30           XSRETURN(1);
197             }