File Coverage

xs.xs
Criterion Covered Total %
statement 155 161 96.2
branch 101 150 67.3
condition n/a
subroutine n/a
pod n/a
total 256 311 82.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6              
7             #include "xs/compat.h"
8              
9             #define MY_CXT_KEY "namespace::clean::xs::_guts" XS_VERSION
10             typedef struct {
11             #ifdef USE_ITHREADS
12             tTHX owner;
13             #endif
14             SV* storage_key;
15             } my_cxt_t;
16              
17             START_MY_CXT;
18              
19             #define NCX_STORAGE "__NAMESPACE_CLEAN_STORAGE_XS"
20             #define NCX_REMOVE (&PL_sv_yes)
21             #define NCX_EXCLUDE (&PL_sv_no)
22              
23             typedef struct {
24             HV* storage;
25             SV* marker;
26             } fn_marker;
27              
28             static int NCX_on_scope_end_normal(pTHX_ SV* sv, MAGIC* mg);
29             static MGVTBL vtscope_normal = {
30             NULL, NULL, NULL, NULL, NCX_on_scope_end_normal
31             };
32              
33             static int NCX_on_scope_end_list(pTHX_ SV* sv, MAGIC* mg);
34             static MGVTBL vtscope_list = {
35             NULL, NULL, NULL, NULL, NCX_on_scope_end_list
36             };
37              
38             static inline GV*
39 44           NCX_storage_glob(pTHX_ HV* stash) {
40             dMY_CXT;
41 44           SV** svp = (SV**)hv_fetch_sv_flags(stash, MY_CXT.storage_key, HV_FETCH_JUST_SV | HV_FETCH_LVALUE);
42              
43 44 100         if (!isGV(*svp)) {
44 20           gv_init_sv((GV*)*svp, stash, MY_CXT.storage_key, GV_ADDMULTI);
45             }
46              
47 44           return (GV*)*svp;
48             }
49              
50             static inline HV*
51 25           NCX_storage_hv(pTHX_ HV* stash) {
52 25           GV* glob = NCX_storage_glob(aTHX_ stash);
53 25 100         return GvHVn(glob);
54             }
55              
56             static void
57 24           NCX_foreach_sub(pTHX_ HV* stash, void (cb)(pTHX_ HE*, void*), void* data) {
58 24           STRLEN hvmax = HvMAX(stash);
59 24           HE** hvarr = HvARRAY(stash);
60             assert(hvarr);
61              
62             HE* he;
63             STRLEN bucket_num;
64 960 100         for (bucket_num = 0; bucket_num <= hvmax; ++bucket_num) {
65 1739 100         for (he = hvarr[bucket_num]; he; he = HeNEXT(he)) {
66 803 50         if (HeVAL(he) == &PL_sv_placeholder) continue;
67              
68 803           GV* gv = (GV*)HeVAL(he);
69 803 100         if (!isGV(gv) || (GvCV(gv) && !GvCVGEN(gv))) {
    100          
    100          
70 121           cb(aTHX_ he, data);
71             }
72             }
73             }
74 24           }
75              
76             static void
77 3           NCX_cb_get_functions(pTHX_ HE* slot, void* hv) {
78 3           GV* gv = (GV*)HeVAL(slot);
79              
80 3 50         if (isGV(gv)) {
81 3           hv_storehek((HV*)hv, HeKEY_hek(slot), newRV_inc((SV*)GvCV(gv)));
82             } else {
83 0           hv_storehek((HV*)hv, HeKEY_hek(slot), SvREFCNT_inc_NN(gv));
84             }
85 3           }
86              
87             static void
88 118           NCX_cb_add_marker(pTHX_ HE* slot, void* data) {
89 118           fn_marker* m = (fn_marker*)data;
90              
91 118           HE* he = (HE*)hv_fetchhek_flags(m->storage, HeKEY_hek(slot), HV_FETCH_EMPTY_HE | HV_FETCH_LVALUE);
92              
93             #ifndef NO_HV_FETCH_EMPTY_HE
94 118 100         if (HeVAL(he) == NULL) {
95             #else
96             if (!SvOK(HeVAL(he))) {
97             SvREFCNT_dec_NN(HeVAL(he));
98             #endif
99 106           HeVAL(he) = m->marker;
100             }
101 118           }
102              
103             static void
104 6           NCX_single_marker(pTHX_ HV* storage, SV* name, SV* marker) {
105 6           HE* he = (HE*)hv_fetch_sv_flags(storage, name, HV_FETCH_EMPTY_HE | HV_FETCH_LVALUE);
106              
107             #ifndef NO_HV_FETCH_EMPTY_HE
108 6 50         if (HeVAL(he) == NULL) {
109             #else
110             if (!SvOK(HeVAL(he))) {
111             SvREFCNT_dec_NN(HeVAL(he));
112             #endif
113 6           HeVAL(he) = marker;
114             }
115 6           }
116              
117             #ifdef DEBUGGER_NEEDS_CV_RENAME
118             static void
119             NCX_fake_subname(pTHX_ HV* fake_stash, GV* gv) {
120             if (!PL_DBsub) return;
121              
122             hv_storehek(fake_stash, GvNAME_HEK(gv), (SV*)gv);
123             GvSTASH(gv) = fake_stash;
124             }
125              
126             static HV*
127             NCX_debugger_fake_stash(pTHX_ HV* old_stash) {
128             HEK* orig_hek = HvNAME_HEK(old_stash);
129             assert(orig_hek);
130              
131             #define FAKE_PREFIX "namespace::clean::xs::d::"
132              
133             char* full_name;
134             Newx(full_name, HEK_LEN(orig_hek) + strlen(FAKE_PREFIX) + 1, char);
135              
136             memcpy(full_name, FAKE_PREFIX, strlen(FAKE_PREFIX));
137             memcpy(full_name + strlen(FAKE_PREFIX), HEK_KEY(orig_hek), HEK_LEN(orig_hek) + 1);
138             assert(full_name[HEK_LEN(orig_hek) + strlen(FAKE_PREFIX)] == '\0');
139              
140             HV* fake_stash = gv_stashpvn(full_name, HEK_LEN(orig_hek) + strlen(FAKE_PREFIX), GV_ADD | HEK_UTF8(orig_hek));
141             assert(fake_stash);
142              
143             Safefree(full_name);
144             #undef FAKE_PREFIX
145              
146             return fake_stash;
147             }
148              
149             #define GLOB_NO_NONSUB(gv) (0)
150             #define DEBUGGER_FAKE_STASH \
151             HV* fake_stash = NCX_debugger_fake_stash(aTHX_ stash);
152             #define NCX_replace_glob_sv(...) NCX_replace_glob_sv_impl(__VA_ARGS__, fake_stash)
153             #define NCX_replace_glob_hek(...) NCX_replace_glob_hek_impl(__VA_ARGS__, fake_stash)
154              
155             #else
156              
157             #define NCX_fake_subname(...)
158             #define GLOB_NO_NONSUB(gv) \
159             !GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIOp(gv) && !GvFORM(gv)
160             #define DEBUGGER_FAKE_STASH
161             #define NCX_replace_glob_sv NCX_replace_glob_sv_impl
162             #define NCX_replace_glob_hek NCX_replace_glob_hek_impl
163              
164             #endif
165              
166             #define NCX_REPLACE_PRE \
167             GV* old_gv = (GV*)HeVAL(he);\
168             \
169             if (!isGV(old_gv) || GLOB_NO_NONSUB(old_gv)) { \
170             hv_deletehek(stash, HeKEY_hek(he), G_DISCARD); \
171             return; \
172             } \
173             \
174             CV* cv = GvCVu(old_gv); \
175             if (!cv) return; \
176             \
177             GV* new_gv = (GV*)newSV(0); \
178              
179             #define NCX_REPLACE_POST \
180             HeVAL(he) = (SV*)new_gv; \
181             \
182             if (GvSV(old_gv)) GvSV(new_gv) = (SV*)SvREFCNT_inc_NN(GvSV(old_gv)); \
183             if (GvAV(old_gv)) GvAV(new_gv) = (AV*)SvREFCNT_inc_NN(GvAV(old_gv)); \
184             if (GvHV(old_gv)) GvHV(new_gv) = (HV*)SvREFCNT_inc_NN(GvHV(old_gv)); \
185             if (GvIOp(old_gv)) GvIOp(new_gv) = (IO*)SvREFCNT_inc_NN(GvIOp(old_gv)); \
186             if (GvFORM(old_gv)) GvFORM(new_gv) = (CV*)SvREFCNT_inc_NN(GvFORM(old_gv)); \
187             \
188             GvCV_set(old_gv, cv); \
189             GvCV_set(new_gv, NULL); \
190             NCX_fake_subname(aTHX_ fake_stash, old_gv); \
191              
192             static void
193             #ifdef DEBUGGER_NEEDS_CV_RENAME
194             NCX_replace_glob_sv_impl(pTHX_ HV* stash, SV* name, HV* fake_stash) {
195             #else
196 2006           NCX_replace_glob_sv_impl(pTHX_ HV* stash, SV* name) {
197             #endif
198 2006           HE* he = hv_fetch_ent(stash, name, 0, 0);
199 2006 100         if (!he) return;
200              
201 1005 50         NCX_REPLACE_PRE;
    50          
    50          
    50          
    50          
    50          
    0          
    0          
202              
203 0           gv_init_sv(new_gv, stash, name, GV_ADDMULTI);
204              
205 0 0         NCX_REPLACE_POST;
    0          
    0          
    0          
    0          
206             }
207              
208             static void
209             #ifdef DEBUGGER_NEEDS_CV_RENAME
210             NCX_replace_glob_hek_impl(pTHX_ HV* stash, HEK* hek, HV* fake_stash) {
211             #else
212 104           NCX_replace_glob_hek_impl(pTHX_ HV* stash, HEK* hek) {
213             #endif
214 104           HE* he = (HE*)hv_fetchhek_flags(stash, hek, 0);
215 104 50         if (!he) return;
216              
217 104 100         NCX_REPLACE_PRE;
    100          
    50          
    50          
    50          
    50          
    50          
    50          
218              
219 2           gv_init_pvn(new_gv, stash, HEK_KEY(hek), HEK_LEN(hek), GV_ADDMULTI | HEK_UTF8(hek));
220              
221 2 50         NCX_REPLACE_POST;
    100          
    100          
    100          
    50          
222             }
223              
224             static int
225 19           NCX_on_scope_end_normal(pTHX_ SV* sv, MAGIC* mg) {
226 19           HV* stash = (HV*)(mg->mg_obj);
227 19           GV* storage_gv = NCX_storage_glob(aTHX_ stash);
228              
229 19           HV* storage = GvHV(storage_gv);
230 19 50         if (!storage) return 0;
231              
232 19           STRLEN hvmax = HvMAX(storage);
233 19           HE** hvarr = HvARRAY(storage);
234 19 100         if (!hvarr) return 0;
235              
236 16           SV* pl_remove = NCX_REMOVE;
237              
238             DEBUGGER_FAKE_STASH;
239              
240             HE* he;
241             STRLEN bucket_num;
242 216 100         for (bucket_num = 0; bucket_num <= hvmax; ++bucket_num) {
243 312 100         for (he = hvarr[bucket_num]; he; he = HeNEXT(he)) {
244             assert(HeVAL(he) == NCX_REMOVE || HeVAL(he) == NCX_EXCLUDE);
245              
246 112 100         if (HeVAL(he) == pl_remove) {
247 104           NCX_replace_glob_hek(aTHX_ stash, HeKEY_hek(he));
248             }
249             }
250             }
251              
252 16           mro_method_changed_in(stash);
253              
254 16           SvREFCNT_dec_NN(storage);
255 16           GvHV(storage_gv) = NULL;
256              
257 16           return 0;
258             }
259              
260             static void
261 21           NCX_register_hook_normal(pTHX_ HV* stash) {
262 21           SV* hints = (SV*)GvHV(PL_hintgv);
263             assert(hints);
264              
265 21 50         if (SvRMAGICAL(hints)) {
266             MAGIC* mg;
267 41 100         for (mg = SvMAGIC(hints); mg; mg = mg->mg_moremagic) {
268 22 100         if (mg->mg_virtual == &vtscope_normal && mg->mg_obj == (SV*)stash) {
    100          
269 2           return;
270             }
271             }
272             }
273              
274 19           sv_magicext(hints, (SV*)stash, PERL_MAGIC_ext, &vtscope_normal, NULL, 0);
275 19           PL_hints |= HINT_LOCALIZE_HH;
276             }
277              
278             static int
279 1001           NCX_on_scope_end_list(pTHX_ SV* sv, MAGIC* mg) {
280 1001           HV* stash = (HV*)(mg->mg_obj);
281 1001           AV* list = (AV*)(mg->mg_ptr);
282             assert(stash && list);
283              
284 1001           SV** items = AvARRAY(list);
285 1001           SSize_t fill = AvFILLp(list);
286             assert(items && fill >= 0);
287              
288             DEBUGGER_FAKE_STASH;
289              
290 3003 100         while (fill-- >= 0) {
291 2002           NCX_replace_glob_sv(aTHX_ stash, *items++);
292             }
293              
294 1001           mro_method_changed_in(stash);
295              
296 1001           return 0;
297             }
298              
299             static void
300 1001           NCX_register_hook_list(pTHX_ HV* stash, AV* list) {
301 1001           sv_magicext((SV*)GvHV(PL_hintgv), (SV*)stash, PERL_MAGIC_ext, &vtscope_list, (const char *)list, HEf_SVKEY);
302 1001           PL_hints |= HINT_LOCALIZE_HH;
303 1001           }
304              
305             MODULE = namespace::clean::xs PACKAGE = namespace::clean::xs
306             PROTOTYPES: DISABLE
307              
308             BOOT:
309             {
310             MY_CXT_INIT;
311 16           MY_CXT.storage_key = newSVpvn_share(NCX_STORAGE, strlen(NCX_STORAGE), 0);
312             #ifdef USE_ITHREADS
313             MY_CXT.owner = aTHX;
314             #endif
315             }
316              
317             void
318             import(SV* self, ...)
319             PPCODE:
320             {
321 1022           HV* stash = CopSTASH(PL_curcop);
322              
323 1022           ++SP;
324 1022           SV* except = NULL;
325             SSize_t processed;
326              
327 2029 100         for (processed = 1; processed < items; processed += 2) {
328 2008           SV* arg = *++SP;
329 2008 50         if (!SvPOK(arg)) break;
330              
331 2008           const char* buf = SvPVX_const(arg);
332 2008 50         if (!SvCUR(arg) || buf[0] != '-') break;
    100          
333              
334 1007 50         if (processed + 1 > items) {
335 0           croak("Not enough arguments for %s option in import() call", buf);
336             }
337              
338 1007 100         if (strEQ(buf, "-cleanee")) {
339 1002           stash = gv_stashsv(*++SP, GV_ADD);
340              
341 5 50         } else if (strEQ(buf, "-except")) {
342 5           except = *++SP;
343              
344             } else {
345 0           croak("Unknown argument %s in import() call", buf);
346             }
347             }
348              
349 1022 100         if (processed < items) {
350 1001           AV* list = newAV();
351 1001           av_extend(list, items - processed - 1);
352 1001           AvFILLp(list) = items - processed - 1;
353              
354 1001           SV** list_data = AvARRAY(list);
355 3003 100         while (++processed <= items) {
356 2002           *list_data++ = SvREFCNT_inc_NN(*SP++);
357             }
358              
359 1001           NCX_register_hook_list(aTHX_ stash, list);
360 1001           SvREFCNT_dec_NN(list); /* refcnt owned by magic now */
361              
362             } else {
363 21           HV* storage = NCX_storage_hv(aTHX_ stash);
364 21 100         if (except) {
365 7 100         if (SvROK(except) && SvTYPE(SvRV(except)) == SVt_PVAV) {
    50          
366 2           AV* except_av = (AV*)SvRV(except);
367 2           SSize_t len = av_len(except_av);
368              
369             SSize_t i;
370 5 100         for (i = 0; i <= len; ++i) {
371 3           SV** svp = av_fetch(except_av, i, 0);
372 3 50         if (svp) NCX_single_marker(aTHX_ storage, *svp, NCX_EXCLUDE);
373             }
374              
375             } else {
376 3           NCX_single_marker(aTHX_ storage, except, NCX_EXCLUDE);
377             }
378             }
379              
380 21           fn_marker m = {storage, NCX_REMOVE};
381              
382 21           NCX_foreach_sub(aTHX_ stash, NCX_cb_add_marker, &m);
383 21           NCX_register_hook_normal(aTHX_ stash);
384             }
385              
386 1022           XSRETURN_YES;
387             }
388              
389             void
390             unimport(SV* self, ...)
391             PPCODE:
392             {
393             HV* stash;
394 2 100         if (items > 2) {
395 1           ++SP;
396 1           SV* arg = *++SP;
397              
398 1 50         if (SvPOK(arg) && strEQ(SvPVX(arg), "-cleanee")) {
    50          
399 1           stash = gv_stashsv(*++SP, 0);
400             } else {
401 0 0         croak("Unknown argument %s in unimport() call", SvPV_nolen(arg));
402             }
403             } else {
404 1           stash = CopSTASH(PL_curcop);
405             }
406              
407 2 50         if (stash) {
408 2           HV* storage = NCX_storage_hv(aTHX_ stash);
409 2           fn_marker m = {storage, NCX_EXCLUDE};
410              
411 2           NCX_foreach_sub(aTHX_ stash, NCX_cb_add_marker, &m);
412             }
413              
414 2           XSRETURN_YES;
415             }
416              
417             void
418             clean_subroutines(SV* self, SV* package, ...)
419             PPCODE:
420             {
421 1           HV* stash = gv_stashsv(package, 0);
422 1 50         if (stash && --items > 1) {
    50          
423 1           SP += 2;
424              
425             DEBUGGER_FAKE_STASH;
426              
427 5 100         while (--items > 0) {
428 4           NCX_replace_glob_sv(aTHX_ stash, *++SP);
429             }
430              
431 1           mro_method_changed_in(stash);
432             }
433              
434 1           XSRETURN_UNDEF;
435             }
436              
437             void
438             get_functions(SV* self, SV* package)
439             PPCODE:
440             {
441 1           HV* hv = newHV();
442            
443 1           HV* stash = gv_stashsv(package, 0);
444 1 50         if (stash) {
445 1           NCX_foreach_sub(aTHX_ stash, NCX_cb_get_functions, hv);
446             }
447              
448 1           PUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
449 1           XSRETURN(1);
450             }
451              
452             void
453             get_class_store(SV* self, SV* package)
454             PPCODE:
455             {
456 2           HV* hv = newHV();
457              
458 2           HV* stash = gv_stashsv(package, 0);
459 2 50         if (stash) {
460 2           HV* storage = NCX_storage_hv(aTHX_ stash);
461              
462 2           HV* exclude = newHV();
463 2           hv_store(hv, "exclude", 7, newRV_noinc((SV*)exclude), 0);
464              
465 2           HV* remove = newHV();
466 2           hv_store(hv, "remove", 6, newRV_noinc((SV*)remove), 0);
467              
468 2           hv_iterinit(storage);
469             HE* he;
470 5 100         while ((he = hv_iternext(storage))) {
471             assert(HeVAL(he) == NCX_EXCLUDE || HeVAL(he) == NCX_REMOVE);
472 3 100         hv_storehek(HeVAL(he) == NCX_EXCLUDE ? exclude : remove, HeKEY_hek(he), &PL_sv_undef);
473             }
474             }
475              
476 2           PUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
477 2           XSRETURN(1);
478             }
479              
480             #ifdef USE_ITHREADS
481              
482             void
483             CLONE(...)
484             PPCODE:
485             {
486             SV* cloned;
487              
488             {
489             dMY_CXT;
490             CLONE_PARAMS params = {NULL, 0, MY_CXT.owner};
491              
492             cloned = sv_dup_inc(MY_CXT.storage_key, ¶ms);
493             }
494              
495             {
496             MY_CXT_CLONE;
497             MY_CXT.owner = aTHX;
498             MY_CXT.storage_key = cloned;
499             }
500              
501             XSRETURN_UNDEF;
502             }
503              
504             #endif /* USE_ITHREADS */