File Coverage

src/xs/Stash.cc
Criterion Covered Total %
statement 74 78 94.8
branch 60 110 54.5
condition n/a
subroutine n/a
pod n/a
total 134 188 71.2


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4             #include
5              
6             #ifndef PERL_VERSION_DECIMAL
7             #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
8             #define PERL_DECIMAL_VERSION PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
9             #endif
10              
11             using panda::string_view;
12             using panda::string;
13              
14             namespace xs {
15              
16 13           Stash::op_proxy& Stash::op_proxy::operator= (SV* val) {
17 13           _throw();
18              
19 13 100         if (!val) {
20 1 50         slot(Scalar());
21 1           return *this;
22             }
23              
24 12 50         if (SvROK(val)) val = SvRV(val);
25 12 100         if (SvTYPE(val) == SVt_PVGV) return operator=((GV*)val);
26 11           slot(val);
27 13           return *this;
28             }
29              
30 7           Stash::op_proxy& Stash::op_proxy::operator= (GV* val) {
31 7           _throw();
32 7 100         if (val) {
33 5           SvREFCNT_inc_simple_void_NN(val);
34 5           SvREFCNT_dec_NN(*ptr);
35 5           *ptr = (SV*)val;
36 5           Glob::operator=(val);
37             } else { // it is not allowed to set NULL to hash element, so nullify all slots
38 2 50         slot(Scalar());
39 2 50         slot(Array());
40 2 50         slot(Hash());
41 2 50         slot(Sub());
42             }
43 7           return *this;
44             }
45              
46 939           void Stash::_promote (GV* gv, const panda::string_view& key) const {
47 939 100         if (!gv || SvTYPE(gv) == SVt_PVGV) return;
    100          
48              
49             // perl [5.22.0 - 5.26.0] had a bug: core dump while promoting !CvNAMED subs. workaround it by adding name by hand
50             #if PERL_DECIMAL_VERSION < PERL_VERSION_DECIMAL(5,26,1) && PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(5,22,0)
51             if (SvROK(gv)) {
52             SV* val = SvRV(gv);
53             if (SvTYPE(val) == SVt_PVCV && !CvNAMED((CV*)val)) { // core-dump in gv_init_pvn with non-named CV
54             U32 hash;
55             PERL_HASH(hash, key.data(), key.length());
56             HEK* hek = share_hek(key.data(), key.length(), hash);
57             ((XPVCV*)MUTABLE_PTR(SvANY(val)))->xcv_gv_u.xcv_hek = hek;
58             CvNAMED_on((CV*)val);
59             CvCVGV_RC_off((CV*)val);
60             CvANON_off((CV*)val);
61             }
62             }
63             #endif
64              
65             // promote SV to GV. Note that prior to perl 5.22, not any SV could be promoted, otherwise gv_init_pvn croaks()
66 36           gv_init_pvn(gv, (HV*)sv, key.data(), key.length(), GV_ADDMULTI); // GV_ADDMULTI suppresses 'used only once' warning
67             }
68              
69 210           string Stash::path () const {
70 210           auto pkg = name();
71 210           int len = pkg.length();
72 210 50         string ret(len+3);
73 210 50         char* dst = ret.buf();
74 210           const char* src = pkg.data();
75 3945 100         for (int i = 0; i < len; ++i) {
76 3735 100         if (*src == ':') {
77 240           *dst = '/';
78 240           ++src;
79 240           ++i;
80             }
81 3495           else *dst = *src;
82 3735           ++dst;
83 3735           ++src;
84             }
85 210           *dst++ = '.';
86 210           *dst++ = 'p';
87 210           *dst++ = 'm';
88 210 50         ret.length(dst-ret.buf());
89 210           return ret;
90             }
91              
92 105           void Stash::mark_as_loaded (const Stash& source) const {
93 105 100         if (!source) throw std::invalid_argument(string("can't register module '") + name() + "': source module doesn't exist");
    50          
    50          
    50          
    50          
94 208 50         auto inc = Stash::root().hash("INC");
    50          
95 208 50         auto realpath = inc.fetch(source.path());
    50          
96 104 50         if (!realpath) throw std::invalid_argument(string("can't register module '") + name() + "': source module '" + source.name() + "' hasn't been registered");
    0          
    0          
    0          
    0          
    0          
    0          
97 104 50         inc.store(path(), realpath);
    50          
98 104           }
99              
100 613           void Stash::inherit (const Stash& parent) {
101 1226 50         auto ISA = array("ISA");
102 613 100         if (!ISA) { // we must create @ISA via gv_fetchpvn_flags, because perl is written like a monkey's shit
103 1090 50         auto fqn = string(name()) + "::ISA";
    50          
104 545 50         ISA = GvAV(gv_fetchpvn_flags(fqn.data(), fqn.length(), GV_ADD, SVt_PVAV));
    50          
105             }
106 613 50         av_push(ISA, Simple::shared(parent.name()).detach()); // can't use ISA.push() syntax, because @ISA is a magical array, otherwise MRO cache won't be cleared
    50          
107 613           }
108              
109 2           void Stash::_throw_nomethod (const panda::string_view& name) const {
110 2 50         throw std::invalid_argument(panda::string("can't locate method '") + name + "' via package '" + this->name() + "'");
    50          
    50          
    50          
    50          
    50          
111             }
112              
113 4           bool Stash::isa (const panda::string_view& parent, U32 hash, int flags) const {
114 4 50         if (name() == parent) return TRUE;
115              
116 4 50         const struct mro_meta*const meta = HvMROMETA((HV*)sv);
117 4           HV *isa = meta->isa;
118              
119 4 50         if (!isa) {
120 0           (void)mro_get_linear_isa((HV*)sv);
121 0           isa = meta->isa;
122             }
123              
124 4 100         if (hv_common(isa, NULL, parent.data(), parent.length(), flags, HV_FETCH_ISEXISTS, NULL, hash)) return TRUE;
125 2           return FALSE;
126              
127             }
128              
129 0           Object Stash::bless () const {
130 0           return Object(sv_bless(newRV_noinc(newSV_type(SVt_PVMG)), (HV*)sv), NONE);
131             }
132              
133 2883           Object Stash::bless (const Sv& what) const {
134 2883 100         if (SvROK(what)) return sv_bless(what, (HV*)sv);
135 2880           else return Object(sv_bless(newRV(what), (HV*)sv), NONE);
136             }
137              
138 5870           void Stash::add_const_sub (const panda::string_view& name, const Sv& _val) {
139 11740 50         auto val = _val;
140 5870           val.readonly(true);
141 5870 50         newCONSTSUB_flags((HV*)sv, name.data(), name.length(), 0, val.detach()); // detach because newCONSTSUB doesn't increment refcnt
142 5870           }
143              
144             }