File Coverage

XSConstructor.xs
Criterion Covered Total %
statement 110 115 95.6
branch 68 148 45.9
condition n/a
subroutine n/a
pod n/a
total 178 263 67.6


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2             #include "xshelper.h"
3              
4             #define IsObject(sv) (SvROK(sv) && SvOBJECT(SvRV(sv)))
5             #define IsArrayRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVAV)
6             #define IsHashRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVHV)
7             #define IsCodeRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVCV)
8              
9             #define XSCON_xc_stash(a) ( (HV*)XSCON_av_at((a), XSCON_XC_STASH) )
10              
11             static HV*
12 37           xscon_buildargs(const char* klass, I32 ax, I32 items) {
13             dTHX;
14             HV* args;
15              
16             /* shift @_ */
17 37           ax++;
18 37           items--;
19              
20 37 100         if(items == 1){
21 14           SV* const args_ref = ST(0);
22 14 50         if(!IsHashRef(args_ref)){
    50          
    50          
23 0           croak("Single parameters to new() must be a HASH ref");
24             }
25 14           args = newHVhv((HV*)SvRV(args_ref));
26 14           sv_2mortal((SV*)args);
27             }
28             else{
29             I32 i;
30              
31 23 50         if( (items % 2) != 0 ){
32 0           croak("Odd number of parameters to new()");
33             }
34              
35 23           args = newHV_mortal();
36 94 100         for(i = 0; i < items; i += 2){
37 71           (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U);
38             }
39              
40             }
41 37           return args;
42             }
43              
44             static SV*
45 37           xscon_create_instance(const char* klass) {
46             dTHX;
47             SV* instance;
48 37           instance = sv_bless( newRV_noinc((SV*)newHV()), gv_stashpv(klass, 1) );
49 37           return sv_2mortal(instance);
50             }
51              
52             static void
53 37           xscon_initialize_object(const char* klass, SV* const object, HV* const args, bool const is_cloning) {
54             dTHX;
55              
56             assert(object);
57             assert(args);
58              
59 37 50         if(mg_find((SV*)args, PERL_MAGIC_tied)){
60 0           croak("You cannot use tied HASH reference as initializing arguments");
61             }
62              
63 37           HV* const stash = gv_stashpv(klass, 1);
64             assert(stash != NULL);
65              
66             I32 i;
67             SV* attr;
68             SV** tmp;
69             char* keyname;
70             STRLEN keylen;
71              
72             /* find out allowed attributes */
73 37           SV** const HAS_globref = hv_fetch(stash, "__XSCON_HAS", 11, 0);
74 37           AV* const HAS_attrs = GvAV(*HAS_globref);
75 37           I32 const HAS_len = av_len(HAS_attrs) + 1;
76              
77             /* find out type constraints */
78 37           SV** const ISA_globref = hv_fetch(stash, "__XSCON_ISA", 11, 0);
79 37           HV* const ISA_attrs = GvHV(*ISA_globref);
80              
81             /* copy allowed attributes */
82 206 100         for (i = 0; i < HAS_len; i++) {
83 173           tmp = av_fetch(HAS_attrs, i, 0);
84             assert(tmp);
85 173           attr = *tmp;
86 173 50         keyname = SvPV(attr, keylen);
87            
88 173 100         if (hv_exists(args, keyname, keylen)) {
89 74           SV** val = hv_fetch(args, keyname, keylen, 0);
90 74           SV* val2 = newSVsv(*val);
91              
92             /* optional isa check */
93 74 100         if (hv_exists(ISA_attrs, keyname, keylen)) {
94 8           SV* val3 = newSVsv(*val);
95 8           SV** const check = hv_fetch(ISA_attrs, keyname, keylen, 0);
96             SV* result;
97              
98 8           dSP;
99             int count;
100 8           ENTER;
101 8           SAVETMPS;
102 8 50         PUSHMARK(SP);
103 8 50         EXTEND(SP, 1);
104 8           PUSHs(sv_2mortal(val3));
105 8           PUTBACK;
106 8           count = call_sv(*check, G_SCALAR);
107 8           result = POPs;
108              
109 8 50         if (!SvTRUE(result)) {
    50          
    0          
    50          
    0          
    0          
    50          
    50          
    50          
    100          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
110 4 50         croak("Value '%s' failed type constraint for '%s'", SvPV_nolen(val2), keyname);
111             }
112            
113 4           PUTBACK;
114 4 50         FREETMPS;
115 4           LEAVE;
116             }
117            
118 70           (void)hv_store((HV *)SvRV(object), keyname, keylen, val2, 0);
119             }
120             }
121              
122             /* find out required attributes */
123 33           SV** const REQ_globref = hv_fetch(stash, "__XSCON_REQUIRED", 16, 0);
124 33           AV* const REQ_attrs = GvAV(*REQ_globref);
125 33           I32 const REQ_len = av_len(REQ_attrs) + 1;
126              
127             /* check required attributes */
128 81 100         for (i = 0; i < REQ_len; i++) {
129 60           tmp = av_fetch(REQ_attrs, i, 0);
130             assert(tmp);
131 60           attr = *tmp;
132 60 50         keyname = SvPV(attr, keylen);
133            
134 60 100         if (!hv_exists((HV *)SvRV(object), keyname, keylen)) {
135 12           croak("Attribute '%s' is required", keyname);
136             }
137             }
138 21           }
139              
140             static void
141 21           xscon_buildall(SV* const object, SV* const args) {
142             dTHX;
143            
144             assert(object);
145             assert(args);
146              
147 21           const char* klass = sv_reftype(SvRV(object), 1);
148 21           HV* const stash = gv_stashpv(klass, 1);
149             assert(stash != NULL);
150            
151             /* get cached stuff */
152 21           SV** const globref = hv_fetch(stash, "__XSCON_BUILD", 13, 0);
153 21           SV* buildall = GvSV(*globref);
154            
155             /* undef in $__XSCON_BUILD means we need to populate it */
156 21 100         if (!SvOK(buildall)) {
    50          
    50          
157 4           dSP;
158             int count;
159 4           ENTER;
160 4           SAVETMPS;
161 4 50         PUSHMARK(SP);
162 4 50         EXTEND(SP, 1);
163 4           PUSHs(object);
164 4           PUTBACK;
165 4           count = call_pv("Class::XSConstructor::populate_build", G_VOID);
166 4           PUTBACK;
167 4 50         FREETMPS;
168 4           LEAVE;
169            
170 4           buildall = GvSV(*globref);
171             }
172            
173 21 50         if (!SvOK(buildall)) {
    0          
    0          
174 0           croak("something should have happened!");
175             }
176            
177 21 100         if (!SvROK(buildall)) {
178 18           return;
179             }
180              
181 3 100         if (hv_exists((HV *)SvRV(args), "__no_BUILD__", 12)) {
182 2           SV** val = hv_fetch((HV *)SvRV(args), "__no_BUILD__", 12, 0);
183 2 50         if (SvOK(*val) && SvTRUE(*val)) {
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    100          
184 1           return;
185             }
186             }
187              
188 2           AV* const builds = (AV*)SvRV(buildall);
189 2           I32 const len = av_len(builds) + 1;
190             SV** tmp;
191             SV* build;
192             I32 i;
193              
194 6 100         for (i = 0; i < len; i++) {
195 4           tmp = av_fetch(builds, i, 0);
196             assert(tmp);
197 4           build = *tmp;
198            
199 4           dSP;
200             int count;
201 4           ENTER;
202 4           SAVETMPS;
203 4 50         PUSHMARK(SP);
204 4 50         EXTEND(SP, 2);
205 4           PUSHs(object);
206 4           PUSHs(args);
207 4           PUTBACK;
208 4           count = call_sv(build, G_VOID);
209 4           PUTBACK;
210 4 50         FREETMPS;
211 4           LEAVE;
212             }
213             }
214              
215             MODULE = Class::XSConstructor PACKAGE = Class::XSConstructor
216              
217             void
218             new_object(SV* klass, ...)
219             CODE:
220             {
221             const char* klassname;
222             SV* args;
223             SV* object;
224              
225 37 50         klassname = SvROK(klass) ? sv_reftype(SvRV(klass), 1) : SvPV_nolen_const(klass);
    50          
226 37           args = newRV_inc((SV*)xscon_buildargs(klassname, ax, items));
227 37           sv_2mortal(args);
228 37           object = xscon_create_instance(klassname);
229 37           xscon_initialize_object(klassname, object, (HV*)SvRV(args), FALSE);
230 21           xscon_buildall(object, args);
231 21           ST(0) = object; /* because object is mortal, we should return it as is */
232 21           XSRETURN(1);
233             }
234              
235             void
236             install_constructor(char* name)
237             CODE:
238             {
239 8 50         if (newXS(name, XS_Class__XSConstructor_new_object, (char*)__FILE__) == NULL)
240 0           croak("ARG! Something went really wrong while installing a new XSUB!");
241             }