File Coverage

lib/meta.xs
Criterion Covered Total %
statement 148 180 82.2
branch 122 232 52.5
condition n/a
subroutine n/a
pod n/a
total 270 412 65.5


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2022 -- leonerd@leonerd.org.uk
5             */
6             #define PERL_NO_GET_CONTEXT
7              
8             #include "EXTERN.h"
9             #include "perl.h"
10             #include "XSUB.h"
11              
12             #define HAVE_PERL_VERSION(R, V, S) \
13             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
14              
15             #if !HAVE_PERL_VERSION(5,16,0)
16             # define true TRUE
17             # define false FALSE
18              
19             /* CvPROTO was just stored in SvPV */
20             # define CvPROTO(cv) SvPVX(cv)
21             # define CvPROTOLEN(cv) SvCUR(cv)
22             /* HvNAMELEN did not exist; stash names cannot contain \0 */
23             # define HvNAMELEN(stash) strlen(HvNAME(stash))
24             /* HvNAME and GvNAME could never be UTF-8 */
25             # define HvNAMEUTF8(hv) 0
26             # define GvNAMEUTF8(gv) 0
27              
28             # define gv_init_sv(gv, stash, sv, flags) \
29             gv_init(gv, stash, SvPV_nolen(sv), SvCUR(sv), SvUTF8(sv) | flags)
30             #endif
31              
32             #ifndef av_count
33             # define av_count(av) (AvFILL(av)+1)
34             #endif
35              
36             #ifndef G_LIST
37             # define G_LIST G_ARRAY
38             #endif
39              
40             /* TODO: Define also for compilers without gcc bracegroups */
41             #define _MUST_SVTYPE_FROM_REFSV(rsv, type, svt) \
42             ({ type sv = (type)(SvUV(SvRV(rsv))); assert(sv && SvTYPE(sv) == svt); sv; })
43              
44             #define MUST_STASH_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, HV *, SVt_PVHV)
45             #define MUST_GV_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, GV *, SVt_PVGV)
46             #define MUST_CV_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, CV *, SVt_PVCV)
47              
48             #define SV_FROM_REFSV(sv) \
49             ((SV *)(SvUV(SvRV(sv))))
50              
51             #define wrap_sv_refsv(sv) S_wrap_sv_refsv(aTHX_ sv)
52 19           SV *S_wrap_sv_refsv(pTHX_ SV *sv)
53             {
54 19           SV *ret = newSV(0);
55             const char *metaclass;
56 19           switch(SvTYPE(sv)) {
57             case SVt_PVGV: metaclass = "meta::glob"; break;
58 3           case SVt_PVCV: metaclass = "meta::subroutine"; break;
59 16           default: metaclass = "meta::variable"; break;
60             }
61 19           return sv_setref_uv(newSV(0), metaclass, PTR2UV(sv));
62             }
63              
64             #ifdef SVf_QUOTEDPREFIX
65             # define CROAK_QUOTED_PREFIX(msg, arg) \
66             croak(msg "%" SVf_QUOTEDPREFIX, arg)
67             #else
68             # define CROAK_QUOTED_PREFIX(msg, arg) \
69             croak(msg "\"%" SVf "\"", arg)
70             #endif
71              
72             #define gv_is_empty(gv) S_gv_is_empty(aTHX_ gv)
73 5           static bool S_gv_is_empty(pTHX_ GV *gv)
74             {
75 5 50         if(SvFAKE(gv) ||
    100          
76 4 50         GvSV(gv) ||
77 4 50         GvAV(gv) ||
78 4 50         GvHV(gv) ||
79 8 50         GvCV(gv) ||
    50          
80 8 50         GvIO(gv) ||
    50          
    50          
81 4           GvFORM(gv))
82             return false;
83              
84             /* TODO: any other safety checks? */
85 4           return true;
86             }
87              
88             MODULE = meta PACKAGE = meta
89              
90             SV *
91             get_package(SV *pkgname)
92             CODE:
93             {
94 15           HV *stash = gv_stashsv(pkgname, GV_ADD);
95 15           RETVAL = newSV(0);
96             // TODO: Do we need to refcnt_inc stash?
97 15           sv_setref_uv(RETVAL, "meta::package", PTR2UV(stash));
98             }
99             OUTPUT:
100             RETVAL
101              
102             MODULE = meta PACKAGE = meta::package
103              
104             SV *
105             name(SV *metapkg)
106             CODE:
107             {
108 3 50         HV *stash = MUST_STASH_FROM_REFSV(metapkg);
109 3 50         RETVAL = newSVpvn_flags(HvNAME(stash), HvNAMELEN(stash), HvNAMEUTF8(stash) ? SVf_UTF8 : 0);
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
110             }
111             OUTPUT:
112             RETVAL
113              
114             SV *
115             get_glob(SV *metapkg, SV *name)
116             ALIAS:
117             can_glob = 0
118             get_glob = 1
119             CODE:
120             {
121 9 50         HV *stash = MUST_STASH_FROM_REFSV(metapkg);
122 9           HE *he = hv_fetch_ent(stash, name, 0, 0);
123 9 100         if(he) {
124 3           GV *gv = (GV *)HeVAL(he);
125             assert(SvTYPE(gv) == SVt_PVGV);
126 3           RETVAL = newSV(0);
127             // TODO: DO we need to refcnt_inc gv?
128 3           sv_setref_uv(RETVAL, "meta::glob", PTR2UV(gv));
129             }
130 6 100         else if(ix)
131 1           CROAK_QUOTED_PREFIX("Package does not contain a glob called ", SVfARG(name));
132             else
133             RETVAL = &PL_sv_undef;
134             }
135             OUTPUT:
136             RETVAL
137              
138             SV *
139             get_symbol(SV *metapkg, SV *name)
140             ALIAS:
141             can_symbol = 0
142             get_symbol = 1
143             CODE:
144             {
145 17 50         HV *stash = MUST_STASH_FROM_REFSV(metapkg);
146 17 50         char sigil = SvPV_nolen(name)[0];
147 17 50         SV *basename = newSVpvn_flags(SvPV_nolen(name) + 1, SvCUR(name) - 1,
148             (SvUTF8(name) ? SVf_UTF8 : 0) | SVs_TEMP);
149             SV *ret = NULL;
150 17           HE *he = hv_fetch_ent(stash, basename, 0, 0);
151 17 100         if(!he)
152             goto missing;
153 12           SV *sv = HeVAL(he);
154              
155 12 50         if(SvTYPE(sv) == SVt_PVGV) {
156             GV *gv = (GV *)sv;
157 12           switch(sigil) {
158 0           case '*': ret = (SV *) gv; break;
159 3           case '$': ret = GvSV(gv); break;
160 3           case '@': ret = (SV *)GvAV(gv); break;
161 4           case '%': ret = (SV *)GvHV(gv); break;
162 2           case '&': ret = (SV *)GvCV(gv); break;
163             }
164             }
165 0 0         else if(SvROK(sv)) {
166             // GV-less optimisation; this is an RV to one kind of element
167 0           SV *rv = SvRV(sv);
168 0           switch(sigil) {
169             case '*': /* We know it isn't an SVt_PVGV */ ret = NULL; break;
170 0 0         case '$': ret = (SvTYPE(rv) <= SVt_PVMG) ? rv : NULL; break;
171 0 0         case '@': ret = (SvTYPE(rv) == SVt_PVAV) ? rv : NULL; break;
172 0 0         case '%': ret = (SvTYPE(rv) == SVt_PVHV) ? rv : NULL; break;
173 0 0         case '&': ret = (SvTYPE(rv) == SVt_PVCV) ? rv : NULL; break;
174             }
175             }
176             else
177 0           croak("TODO: Not sure what to do with SvTYPE(sv)=%d\n", SvTYPE(sv));
178              
179             missing:
180 17 100         if(ret)
181 9           RETVAL = wrap_sv_refsv(ret);
182 8 100         else if(ix)
183 2           CROAK_QUOTED_PREFIX("Package has no symbol named ", SVfARG(name));
184             else
185             RETVAL = &PL_sv_undef;
186             }
187             OUTPUT:
188             RETVAL
189              
190             SV *
191             add_symbol(SV *metapkg, SV *name, SV *value)
192             CODE:
193             {
194 9 50         HV *stash = MUST_STASH_FROM_REFSV(metapkg);
195 9 50         char sigil = SvPV_nolen(name)[0];
196 9 50         if(!SvROK(value))
197 0           croak("Expected a reference for the new value to add_symbol");
198              
199 9           SV *sv = SvRV(value);
200 9           switch(sigil) {
201             case '*':
202 0           croak("TODO: Cannot currently cope with adding GLOBs via ->add_symbol");
203             break;
204             case '$':
205 4 50         if(SvTYPE(sv) > SVt_PVMG)
206 0           croak("Expected a SCALAR reference for the new value to add_symbol('$...')");
207             break;
208             case '@':
209 2 50         if(SvTYPE(sv) != SVt_PVAV)
210 0           croak("Expected a ARRAY reference for the new value to add_symbol('@...')");
211             break;
212             case '%':
213 2 50         if(SvTYPE(sv) != SVt_PVHV)
214 0           croak("Expected a HASH reference for the new value to add_symbol('%...')");
215             break;
216             case '&':
217 1 50         if(SvTYPE(sv) != SVt_PVCV)
218 0           croak("Expected a CODE reference for the new value to add_symbol('&...')");
219             break;
220             default:
221 0           croak("Unrecognised name sigil for add_symbol");
222             }
223              
224 9 50         SV *basename = newSVpvn_flags(SvPV_nolen(name) + 1, SvCUR(name) - 1,
225             (SvUTF8(name) ? SVf_UTF8 : 0) | SVs_TEMP);
226 9           HE *he = hv_fetch_ent(stash, basename, GV_ADD, 0);
227 9           GV *gv = (GV *)HeVAL(he);
228 9 100         if(SvTYPE(gv) != SVt_PVGV) {
229 5           gv_init_sv(gv, stash, basename, 0);
230             }
231              
232 9           switch(sigil) {
233             case '$':
234 4 100         if(GvSV(gv))
235 1           CROAK_QUOTED_PREFIX("Already have a symbol named ", SVfARG(name));
236 3           GvSV(gv) = SvREFCNT_inc(sv);
237 3           break;
238             case '@':
239 2 50         if(GvAV(gv))
240 0           CROAK_QUOTED_PREFIX("Already have a symbol named ", SVfARG(name));
241 2           GvAV(gv) = (AV *)SvREFCNT_inc(sv);
242 2           break;
243             case '%':
244 2 50         if(GvHV(gv))
245 0           CROAK_QUOTED_PREFIX("Already have a symbol named ", SVfARG(name));
246 2           GvHV(gv) = (HV *)SvREFCNT_inc(sv);
247 2           break;
248             case '&':
249 1 50         if(GvCV(gv))
250 0           CROAK_QUOTED_PREFIX("Already have a symbol named ", SVfARG(name));
251 1           GvCV_set(gv, (CV *)SvREFCNT_inc(sv));
252 1           break;
253             }
254 8           RETVAL = wrap_sv_refsv(sv);
255             }
256             OUTPUT:
257             RETVAL
258              
259             void
260             remove_symbol(SV *metapkg, SV *name)
261             CODE:
262             {
263 7 50         HV *stash = MUST_STASH_FROM_REFSV(metapkg);
264 7 50         char sigil = SvPV_nolen(name)[0];
265 7 50         SV *basename = newSVpvn_flags(SvPV_nolen(name) + 1, SvCUR(name) - 1,
266             (SvUTF8(name) ? SVf_UTF8 : 0) | SVs_TEMP);
267             SV *ret = NULL;
268 7           HE *he = hv_fetch_ent(stash, basename, 0, 0);
269 7 100         if(!he)
270             goto missing;
271 6           SV *sv = HeVAL(he);
272              
273 6 50         if(SvTYPE(sv) == SVt_PVGV) {
274             GV *gv = (GV *)sv;
275             SV *sv = NULL;
276 6           switch(sigil) {
277 0           case '*': croak("TODO: Cannot ->remove_symbol on a glob"); break;
278             case '$':
279 1           sv = GvSV(gv); GvSV(gv) = NULL;
280 1           break;
281             case '@':
282 3           sv = (SV *)GvAV(gv); GvAV(gv) = NULL;
283 3           break;
284             case '%':
285 1           sv = (SV *)GvHV(gv); GvHV(gv) = NULL;
286 1           break;
287             case '&':
288 1           sv = (SV *)GvCV(gv); GvCV_set(gv, NULL);
289 1           break;
290             }
291              
292 6 100         if(!sv)
293             missing:
294 2           CROAK_QUOTED_PREFIX("Cannot remove non-existing symbol from package: ", SVfARG(name));
295              
296             SvREFCNT_dec(sv);
297              
298             /* TODO: Perl core has a gv_try_downgrade() we could call here, but XS
299             * modules can't see it
300             */
301 5 100         if(gv_is_empty(gv))
302 4           hv_delete_ent(stash, basename, G_DISCARD, 0);
303             }
304 0 0         else if(SvROK(sv)) {
305             // GV-less optimisation; this is an RV to one kind of element
306 0           SV *rv = SvRV(sv);
307 0           switch(sigil) {
308             case '*': /* We know it isn't a SVt_PVGV */ goto missing; break;
309 0 0         case '$': if(SvTYPE(rv) > SVt_PVMG) goto missing; break;
310 0 0         case '@': if(SvTYPE(rv) != SVt_PVAV) goto missing; break;
311 0 0         case '%': if(SvTYPE(rv) != SVt_PVHV) goto missing; break;
312 0 0         case '&': if(SvTYPE(rv) != SVt_PVCV) goto missing; break;
313             }
314              
315 0           hv_delete_ent(stash, basename, G_DISCARD, 0);
316             }
317             else
318 0           croak("TODO: Not sure what to do with SvTYPE(sv)=%d\n", SvTYPE(sv));
319             }
320              
321             MODULE = meta PACKAGE = meta::symbol
322              
323             bool
324             is_scalar(SV *metasym)
325             CODE:
326             {
327 1 50         SV *sv = SV_FROM_REFSV(metasym);
328 1           RETVAL = SvTYPE(sv) <= SVt_PVMG;
329             }
330             OUTPUT:
331             RETVAL
332              
333             bool
334             _is_type(SV *metasym)
335             ALIAS:
336             is_glob = SVt_PVGV
337             is_array = SVt_PVAV
338             is_hash = SVt_PVHV
339             is_subroutine = SVt_PVCV
340             CODE:
341             {
342 7 50         SV *sv = SV_FROM_REFSV(metasym);
343 7           RETVAL = SvTYPE(sv) == ix;
344             }
345             OUTPUT:
346             RETVAL
347              
348             SV *
349             reference(SV *metasym)
350             CODE:
351             {
352 9 50         SV *sv = SV_FROM_REFSV(metasym);
353 9           RETVAL = newRV_inc(sv);
354             }
355             OUTPUT:
356             RETVAL
357              
358             MODULE = meta PACKAGE = meta::glob
359              
360             SV *
361             basename(SV *metaglob)
362             CODE:
363             {
364 1 50         GV *gv = MUST_GV_FROM_REFSV(metaglob);
365 1 50         RETVAL = newSVpvn_flags(GvNAME(gv), GvNAMELEN(gv), GvNAMEUTF8(gv) ? SVf_UTF8 : 0);
366             }
367             OUTPUT:
368             RETVAL
369              
370             SV *
371             _get_slot(SV *metaglob)
372             ALIAS:
373             can_scalar = 1
374             get_scalar = 0x81
375             can_array = 2
376             get_array = 0x82
377             can_hash = 3
378             get_hash = 0x83
379             can_code = 4
380             get_code = 0x84
381             CODE:
382             {
383 4 50         GV *gv = MUST_GV_FROM_REFSV(metaglob);
384             SV *ret;
385 4           switch(ix & ~0x80) {
386 1           case 1: ret = GvSV(gv); break;
387 1           case 2: ret = (SV *)GvAV(gv); break;
388 2           case 3: ret = (SV *)GvHV(gv); break;
389 0           case 4: ret = (SV *)GvCV(gv); break;
390             }
391 4 100         if(ret)
392 2           RETVAL = wrap_sv_refsv(ret);
393 2 100         else if(ix & 0x80)
394 1           croak("Glob does not have a %s slot",
395 1           ((const char *[]){NULL, "scalar", "array", "hash", "code"})[ix & ~0x80]);
396             else
397             RETVAL = &PL_sv_undef;
398             }
399             OUTPUT:
400             RETVAL
401              
402             MODULE = meta PACKAGE = meta::variable
403              
404             void
405             value(SV *metavar)
406             PPCODE:
407             {
408 5 50         if(GIMME_V == G_VOID)
    50          
409             // TODO: warn?
410 0           XSRETURN(0);
411              
412             /* TODO: all of the-below is super-fragile and probably doesn't work
413             * properly with tied scalars/arrays/hashes. Eugh.
414             */
415              
416 5 50         SV *sv = SV_FROM_REFSV(metavar);
417 5 100         if(SvTYPE(sv) <= SVt_PVMG) {
418 1           SV *ret = sv_mortalcopy(sv);
419 1 50         XPUSHs(ret);
420 1           XSRETURN(1);
421             }
422 4 100         else if(SvTYPE(sv) == SVt_PVAV) {
423             /* Array */
424             AV *av = (AV *)sv;
425 2 50         UV count = av_count(av);
426              
427 2 50         if(GIMME_V == G_SCALAR) {
    100          
428 1 50         mXPUSHu(count);
429 1           XSRETURN(1);
430             }
431 1 50         EXTEND(SP, count);
432 4 100         for(UV i = 0; i < count; i++)
433 3           PUSHs(sv_mortalcopy(*av_fetch(av, i, 0)));
434 1           XSRETURN(count);
435             }
436 2 50         else if(SvTYPE(sv) == SVt_PVHV) {
437             /* Hash */
438             HV *hv = (HV *)sv;
439             UV count = 0;
440 2 50         U8 gimme = GIMME_V;
441              
442             HE *he;
443 2           hv_iterinit(hv);
444 6 100         while((he = hv_iternext(hv))) {
445 4 50         SV *key = HeSVKEY(he);
    50          
446 4 50         if(!key)
447 4           key = newSVpvn_flags(HeKEY(he), HeKLEN(he), HeKFLAGS(he) | SVs_TEMP);
448              
449 4 100         if(gimme == G_LIST) {
450 2 50         EXTEND(SP, 2);
451 2           PUSHs(key);
452 2           PUSHs(HeVAL(he));
453             }
454 4           count++;
455             }
456              
457 2 100         if(gimme == G_LIST)
458 1           XSRETURN(count * 2);
459              
460 1           mPUSHu(count);
461 1           XSRETURN(1);
462             }
463             else
464 0           croak("Argh unrecognised SvTYPE(sv)=%d", SvTYPE(sv));
465             }
466              
467             MODULE = meta PACKAGE = meta::subroutine
468              
469             SV *
470             subname(SV *metasub)
471             CODE:
472             {
473 1 50         CV *cv = MUST_CV_FROM_REFSV(metasub);
474              
475             GV *gv = CvGV(cv);
476 1 50         if(!gv)
477             RETVAL = &PL_sv_undef;
478             else
479 1 50         RETVAL = newSVpvf("%s::%s", HvNAME(GvSTASH(gv)), GvNAME(gv));
    50          
    50          
    0          
    50          
    50          
480             }
481             OUTPUT:
482             RETVAL
483              
484             SV *
485             prototype(SV *metasub)
486             CODE:
487             {
488 1 50         CV *cv = MUST_CV_FROM_REFSV(metasub);
489              
490 1 50         if(!SvPOK(cv))
491             RETVAL = &PL_sv_undef;
492             else
493 1 50         RETVAL = newSVpvn_flags(CvPROTO(cv), CvPROTOLEN(cv), SvUTF8(cv));
    50          
    50          
    50          
    50          
    50          
494             }
495             OUTPUT:
496             RETVAL