File Coverage

Util.xs
Criterion Covered Total %
statement 18 173 10.4
branch 13 256 5.0
condition n/a
subroutine n/a
pod n/a
total 31 429 7.2


line stmt bran cond sub pod time code
1             #include "typetiny.h"
2             #include "xs_version.h"
3              
4             #define MY_CXT_KEY "Type::Tiny::XS::Util::_guts" XS_VERSION
5             typedef struct {
6             HV* metas;
7             } my_cxt_t;
8             START_MY_CXT
9              
10             #define ISA_CACHE "::LINEALIZED_ISA_CACHE::"
11              
12             #ifdef no_mro_get_linear_isa
13             AV*
14             typetiny_mro_get_linear_isa(pTHX_ HV* const stash){
15             GV* const cachegv = *(GV**)hv_fetchs(stash, ISA_CACHE, TRUE);
16             AV* isa;
17             SV* gen;
18             CV* get_linear_isa;
19              
20             if(!isGV(cachegv))
21             gv_init(cachegv, stash, ISA_CACHE, sizeof(ISA_CACHE)-1, TRUE);
22              
23             isa = GvAVn(cachegv);
24             gen = GvSVn(cachegv);
25              
26              
27             if(SvIOK(gen) && SvIVX(gen) == (IV)mro_get_pkg_gen(stash)){
28             return isa; /* returns the cache if available */
29             }
30             else{
31             SvREFCNT_dec(isa);
32             GvAV(cachegv) = isa = newAV();
33             }
34              
35             get_linear_isa = get_cv("Type::Tiny::XS::Util::get_linear_isa", TRUE);
36              
37             {
38             SV* avref;
39             dSP;
40              
41             ENTER;
42             SAVETMPS;
43              
44             PUSHMARK(SP);
45             mXPUSHp(HvNAME_get(stash), HvNAMELEN_get(stash));
46             PUTBACK;
47              
48             call_sv((SV*)get_linear_isa, G_SCALAR);
49              
50             SPAGAIN;
51             avref = POPs;
52             PUTBACK;
53              
54             if(IsArrayRef(avref)){
55             AV* const av = (AV*)SvRV(avref);
56             I32 const len = AvFILLp(av) + 1;
57             I32 i;
58              
59             for(i = 0; i < len; i++){
60             HV* const stash = gv_stashsv(AvARRAY(av)[i], FALSE);
61             if(stash)
62             av_push(isa, newSVpv(HvNAME(stash), 0));
63             }
64             SvREADONLY_on(isa);
65             }
66             else{
67             Perl_croak(aTHX_ "Type::Tiny::XS::Util::get_linear_isa() didn't return an ARRAY reference");
68             }
69              
70             FREETMPS;
71             LEAVE;
72             }
73              
74             sv_setiv(gen, (IV)mro_get_pkg_gen(stash));
75             return isa;
76             }
77             #endif /* !no_mor_get_linear_isa */
78              
79             #ifdef DEBUGGING
80             SV*
81             typetiny_av_at_safe(pTHX_ AV* const av, I32 const ix){
82             assert(av);
83             assert(SvTYPE(av) == SVt_PVAV);
84             assert(AvMAX(av) >= ix);
85             return AvARRAY(av)[ix] ? AvARRAY(av)[ix] : &PL_sv_undef;
86             }
87             #endif
88              
89             void
90 0           typetiny_throw_error(SV* const metaobject, SV* const data /* not used */, const char* const fmt, ...){
91             dTHX;
92             va_list args;
93             SV* message;
94              
95             assert(metaobject);
96             assert(fmt);
97              
98 0           va_start(args, fmt);
99 0           message = vnewSVpvf(fmt, &args);
100 0           va_end(args);
101              
102             {
103 0           dSP;
104 0 0         PUSHMARK(SP);
105 0 0         EXTEND(SP, 6);
106              
107 0           PUSHs(metaobject);
108 0           mPUSHs(message);
109              
110 0 0         if(data){ /* extra arg, might be useful for debugging */
111 0           mPUSHs(newSVpvs("data"));
112 0           PUSHs(data);
113 0           mPUSHs(newSVpvs("depth"));
114 0           mPUSHi(-1);
115             }
116 0           PUTBACK;
117 0 0         if(SvOK(metaobject)) {
    0          
    0          
118 0           call_method("throw_error", G_VOID);
119             }
120             else {
121 0           call_pv("Type::Tiny::XS::Util::throw_error", G_VOID);
122             }
123 0           croak("throw_error() did not throw the error (%"SVf")", message);
124             }
125             }
126              
127             #if (PERL_BCDVERSION < 0x5014000)
128             /* workaround Perl-RT #69939 */
129             I32
130             typetiny_call_sv_safe(pTHX_ SV* const sv, I32 const flags) {
131             I32 count;
132             ENTER;
133             /* Don't do SAVETMPS */
134              
135             SAVEGENERICSV(ERRSV); /* local $@ */
136             ERRSV = newSV(0);
137              
138             count = Perl_call_sv(aTHX_ sv, flags | G_EVAL);
139              
140             if(sv_true(ERRSV)){
141             SV* const err = sv_mortalcopy(ERRSV);
142             LEAVE;
143             sv_setsv(ERRSV, err);
144             croak(NULL); /* rethrow */
145             }
146              
147             LEAVE;
148              
149             return count;
150             }
151             #endif
152              
153             void
154 1           typetiny_must_defined(pTHX_ SV* const value, const char* const name) {
155             assert(value);
156             assert(name);
157              
158 1 50         SvGETMAGIC(value);
    0          
159 1 50         if(!SvOK(value)){
    0          
    0          
160 0           croak("You must define %s", name);
161             }
162 1           }
163              
164             void
165 0           typetiny_must_ref(pTHX_ SV* const value, const char* const name, svtype const t) {
166             assert(value);
167             assert(name);
168              
169 0 0         SvGETMAGIC(value);
    0          
170 0 0         if(!(SvROK(value) && (t == SVt_NULL || SvTYPE(SvRV(value)) == t))) {
    0          
    0          
171 0 0         croak("You must pass %s, not %s",
172 0 0         name, SvOK(value) ? SvPV_nolen(value) : "undef");
    0          
    0          
173             }
174 0           }
175              
176              
177             bool
178 0           typetiny_is_class_loaded(pTHX_ SV * const klass){
179             HV *stash;
180             GV** gvp;
181             HE* he;
182              
183 0 0         if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
    0          
184 0           return FALSE;
185             }
186              
187 0           stash = gv_stashsv(klass, FALSE);
188 0 0         if (!stash) {
189 0           return FALSE;
190             }
191              
192 0 0         if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
193 0 0         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
    0          
    0          
    0          
    0          
194 0           return TRUE;
195             }
196             }
197              
198 0 0         if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
199 0 0         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
    0          
    0          
200 0           return TRUE;
201             }
202             }
203              
204 0           hv_iterinit(stash);
205 0 0         while(( he = hv_iternext(stash) )){
206 0           GV* const gv = (GV*)HeVAL(he);
207              
208 0 0         if(isGV(gv)){
209 0 0         if(GvCVu(gv)){ /* is GV and has CV */
    0          
210 0           hv_iterinit(stash); /* reset */
211 0           return TRUE;
212             }
213             }
214 0 0         else if(SvOK(gv)){ /* is a stub or constant */
    0          
    0          
215 0           hv_iterinit(stash); /* reset */
216 0           return TRUE;
217             }
218             }
219 0           return FALSE;
220             }
221              
222              
223             SV*
224 0           typetiny_call0 (pTHX_ SV* const self, SV* const method) {
225 0           dSP;
226             SV *ret;
227              
228 0 0         PUSHMARK(SP);
229 0 0         XPUSHs(self);
230 0           PUTBACK;
231              
232 0           call_sv_safe(method, G_SCALAR | G_METHOD);
233              
234 0           SPAGAIN;
235 0           ret = POPs;
236 0           PUTBACK;
237              
238 0           return ret;
239             }
240              
241             SV*
242 0           typetiny_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
243 0           dSP;
244             SV *ret;
245              
246 0 0         PUSHMARK(SP);
247 0 0         EXTEND(SP, 2);
248 0           PUSHs(self);
249 0           PUSHs(arg1);
250 0           PUTBACK;
251              
252 0           call_sv_safe(method, G_SCALAR | G_METHOD);
253              
254 0           SPAGAIN;
255 0           ret = POPs;
256 0           PUTBACK;
257              
258 0           return ret;
259             }
260              
261             int
262 0           typetiny_predicate_call(pTHX_ SV* const self, SV* const method) {
263 0           return sv_true( mcall0(self, method) );
264             }
265              
266             SV*
267 0           typetiny_get_metaclass(pTHX_ SV* metaclass_name){
268             dMY_CXT;
269             HE* he;
270              
271             assert(metaclass_name);
272             assert(MY_CXT.metas);
273              
274 0 0         if(IsObject(metaclass_name)){
    0          
275 0           HV* const stash = SvSTASH(SvRV(metaclass_name));
276              
277 0 0         metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
278 0           sv_2mortal(metaclass_name);
279             }
280              
281 0           he = hv_fetch_ent(MY_CXT.metas, metaclass_name, FALSE, 0U);
282              
283 0 0         return he ? HeVAL(he) : &PL_sv_undef;
284             }
285              
286             MAGIC*
287 0           typetiny_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
288             MAGIC* mg;
289              
290             assert(sv != NULL);
291 0 0         for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
292 0 0         if(mg->mg_virtual == vtbl){
293 0           return mg;
294             }
295             }
296              
297 0 0         if(flags & TYPETINYf_DIE_ON_FAIL){
298 0           croak("typetiny_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
299             }
300 0           return NULL;
301             }
302              
303             GV*
304 0           typetiny_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
305 0 0         GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
306              
307 0 0         if(gvp){
308 0 0         if(!isGV(*gvp)){
309 0           gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
310             }
311 0           return *gvp;
312             }
313             else{
314 0           return NULL;
315             }
316             }
317              
318             void
319 0           typetiny_install_sub(pTHX_ GV* const gv, SV* const code_ref) {
320             CV* cv;
321              
322             assert(gv != NULL);
323             assert(code_ref != NULL);
324             assert(isGV(gv));
325             assert(IsCodeRef(code_ref));
326              
327 0 0         if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */
    0          
328 0           SvREFCNT_dec(GvCV(gv));
329 0           GvCV_set(gv, NULL);
330             }
331              
332 0           sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */
333              
334             /* name the CODE ref if it's anonymous */
335 0           cv = (CV*)SvRV(code_ref);
336 0 0         if(CvANON(cv)
337 0 0         && CvGV(cv) /* a cv under construction has no gv */ ){
338             HV* dbsub;
339              
340             /* update %DB::sub to make NYTProf happy */
341 0 0         if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON))
342 0 0         && PL_DBsub && (dbsub = GvHV(PL_DBsub))
    0          
343             ){
344             /* see Perl_newATTRSUB() in op.c */
345 0           SV* const subname = sv_newmortal();
346             HE* orig;
347              
348 0           gv_efullname3(subname, CvGV(cv), NULL);
349 0           orig = hv_fetch_ent(dbsub, subname, FALSE, 0U);
350 0 0         if(orig){
351 0           gv_efullname3(subname, gv, NULL);
352 0           (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U);
353 0           SvREFCNT_inc_simple_void_NN(HeVAL(orig));
354             }
355             }
356              
357 0           CvGV_set(cv, gv);
358 0           CvANON_off(cv);
359             }
360 0           }
361              
362             MODULE = Type::Tiny::XS::Util PACKAGE = Type::Tiny::XS::Util
363              
364             PROTOTYPES: DISABLE
365             VERSIONCHECK: DISABLE
366              
367             BOOT:
368             {
369             MY_CXT_INIT;
370 18           MY_CXT.metas = NULL;
371             }
372              
373             void
374             __register_metaclass_storage(HV* metas, bool cloning)
375             CODE:
376             {
377 0 0         if(cloning){
378             MY_CXT_CLONE;
379 0           MY_CXT.metas = NULL;
380             }
381             {
382             dMY_CXT;
383 0 0         if(MY_CXT.metas && ckWARN(WARN_REDEFINE)){
    0          
384 0           Perl_warner(aTHX_ packWARN(WARN_REDEFINE), "Metaclass storage more than once");
385             }
386 0           MY_CXT.metas = metas;
387 0           SvREFCNT_inc_simple_void_NN(metas);
388             }
389             }
390              
391             bool
392             is_valid_class_name(SV* sv)
393             CODE:
394             {
395 1 50         SvGETMAGIC(sv);
    0          
396 2 50         if(SvPOKp(sv) && SvCUR(sv) > 0){
    50          
397             UV i;
398 1           RETVAL = TRUE;
399 4 100         for(i = 0; i < SvCUR(sv); i++){
400 3           char const c = SvPVX(sv)[i];
401 3 50         if(!(isALNUM(c) || c == ':')){
    0          
402 0           RETVAL = FALSE;
403 0           break;
404             }
405             }
406             }
407             else{
408 0           RETVAL = SvNIOKp(sv) ? TRUE : FALSE;
409             }
410             }
411             OUTPUT:
412             RETVAL
413              
414             bool
415             is_class_loaded(SV* sv)
416              
417             void
418             get_code_info(CV* code)
419             PREINIT:
420             GV* gv;
421             HV* stash;
422             PPCODE:
423 0 0         if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
    0          
    0          
424 0 0         EXTEND(SP, 2);
425 0 0         mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
426 0           mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
427             }
428              
429             SV*
430             get_code_package(CV* code)
431             PREINIT:
432             HV* stash;
433             CODE:
434 0 0         if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
    0          
    0          
435 0 0         RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
436             }
437             else{
438 0           RETVAL = &PL_sv_no;
439             }
440             OUTPUT:
441             RETVAL
442              
443             CV*
444             get_code_ref(SV* package, SV* name)
445             CODE:
446             {
447             HV* stash;
448             STRLEN name_len;
449             const char* name_pv;
450             GV* gv;
451              
452 0           must_defined(package, "a package name");
453 0           must_defined(name, "a subroutine name");
454              
455 0           stash = gv_stashsv(package, FALSE);
456 0 0         if(!stash){
457 0           XSRETURN_UNDEF;
458             }
459              
460 0 0         name_pv = SvPV_const(name, name_len);
461 0           gv = stash_fetch(stash, name_pv, name_len, FALSE);
462 0 0         RETVAL = gv ? GvCVu(gv) : NULL;
    0          
463              
464 0 0         if(!RETVAL){
465 0           XSRETURN_UNDEF;
466             }
467             }
468             OUTPUT:
469             RETVAL
470              
471             void
472             generate_isa_predicate_for(SV* arg, SV* predicate_name = NULL)
473             ALIAS:
474             generate_isa_predicate_for = 0
475             generate_can_predicate_for = 1
476             PPCODE:
477             {
478 1           const char* name_pv = NULL;
479             CV* xsub;
480              
481 1 50         must_defined(arg, ix == 0 ? "a class_name" : "method names");
482              
483 1 50         if(predicate_name){
484 0           must_defined(predicate_name, "a predicate name");
485 0 0         name_pv = SvPV_nolen_const(predicate_name);
486             }
487              
488 1 50         if(ix == 0){
489 1           xsub = typetiny_generate_isa_predicate_for(aTHX_ arg, name_pv);
490             }
491             else{
492 0           xsub = typetiny_generate_can_predicate_for(aTHX_ arg, name_pv);
493             }
494              
495 1 50         if(predicate_name == NULL){ /* anonymous predicate */
496 1 50         mXPUSHs( newRV_inc((SV*)xsub) );
497             }
498             }
499              
500             # This xsub will redefine &Type::Tiny::XS::Util::install_subroutines()
501             void
502             install_subroutines(SV* into, ...)
503             CODE:
504             {
505             HV* stash;
506             I32 i;
507              
508 0           must_defined(into, "a package name");
509 0           stash = gv_stashsv(into, TRUE);
510              
511 0 0         if( ((items-1) % 2) != 0 ){
512 0           croak_xs_usage(cv, "into, name => coderef [, other_name, other_coderef ...]");
513             }
514              
515 0 0         for(i = 1; i < items; i += 2) {
516 0           SV* const name = ST(i);
517 0           SV* const code = ST(i+1);
518             STRLEN len;
519             const char* pv;
520             GV* gv;
521              
522 0           must_defined(name, "a subroutine name");
523 0           must_ref(code, "a CODE reference", SVt_PVCV);
524              
525 0 0         pv = SvPV_const(name, len);
526 0           gv = stash_fetch(stash, pv, len, TRUE);
527              
528 0           typetiny_install_sub(aTHX_ gv, code);
529             }
530             }