_get_code_ref() and get_linear_isa() in XS
[gitmo/Mouse.git] / xs-src / mouse_util.xs
1 #include "mouse.h"
2
3 #define ISA_CACHE "::LINEALIZED_ISA_CACHE::"
4
5 #ifndef no_mro_get_linear_isa
6 AV*
7 mouse_mro_get_linear_isa(pTHX_ HV* const stash){
8         GV* const cachegv = *(GV**)hv_fetchs(stash, ISA_CACHE, TRUE);
9         AV* isa;
10         SV* gen;
11         CV* get_linear_isa;
12
13         if(!isGV(cachegv))
14                 gv_init(cachegv, stash, ISA_CACHE, sizeof(ISA_CACHE)-1, TRUE);
15
16         isa = GvAVn(cachegv);
17         gen = GvSVn(cachegv);
18
19
20         if(SvIOK(gen) && SvIVX(gen) == (IV)mro_get_pkg_gen(stash)){
21                 return isa; /* returns the cache if available */
22         }
23         else{
24                 SvREADONLY_off(isa);
25                 av_clear(isa);
26         }
27
28         get_linear_isa = get_cv("Mouse::Util::get_linear_isa", TRUE);
29
30         {
31                 SV* avref;
32                 dSP;
33
34                 ENTER;
35                 SAVETMPS;
36
37                 PUSHMARK(SP);
38                 mXPUSHp(HvNAME_get(stash), HvNAMELEN_get(stash));
39                 PUTBACK;
40
41                 call_sv((SV*)get_linear_isa, G_SCALAR);
42
43                 SPAGAIN;
44                 avref = POPs;
45                 PUTBACK;
46
47                 if(SvROK(avref) && SvTYPE(SvRV(avref)) == SVt_PVAV){
48                         AV* const av  = (AV*)SvRV(avref);
49                         I32 const len = AvFILLp(av) + 1;
50                         I32 i;
51
52                         for(i = 0; i < len; i++){
53                                 HV* const stash = gv_stashsv(AvARRAY(av)[i], FALSE);
54                                 if(stash)
55                                         av_push(isa, newSVpv(HvNAME(stash), 0));
56                         }
57                         SvREADONLY_on(isa);
58                 }
59                 else{
60                         Perl_croak(aTHX_ "Mouse:Util::get_linear_isa() didn't return an ARRAY reference");
61                 }
62
63                 FREETMPS;
64                 LEAVE;
65         }
66
67         sv_setiv(gen, (IV)mro_get_pkg_gen(stash));
68         return GvAV(cachegv);
69 }
70 #endif /* !no_mor_get_linear_isa */
71
72
73 /* equivalent to "blessed($x) && $x->isa($klass)" */
74 bool
75 mouse_is_instance_of(pTHX_ SV* const sv, SV* const klass){
76     assert(sv);
77     assert(klass);
78
79     if(IsObject(sv) && SvOK(klass)){
80         bool ok;
81
82         ENTER;
83         SAVETMPS;
84
85         ok = SvTRUEx(mcall1s(sv, "isa", klass));
86
87         FREETMPS;
88         LEAVE;
89
90         return ok;
91     }
92
93     return FALSE;
94 }
95
96
97 bool
98 mouse_is_class_loaded(pTHX_ SV * const klass){
99     HV *stash;
100     GV** gvp;
101     HE* he;
102
103     if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
104         return FALSE;
105     }
106
107     stash = gv_stashsv(klass, FALSE);
108     if (!stash) {
109         return FALSE;
110     }
111
112     if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
113         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
114             return TRUE;
115         }
116     }
117
118     if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
119         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
120             return TRUE;
121         }
122     }
123
124     hv_iterinit(stash);
125     while(( he = hv_iternext(stash) )){
126         GV* const gv = (GV*)HeVAL(he);
127
128         if(isGV(gv)){
129             if(GvCVu(gv)){
130                 return TRUE;
131             }
132         }
133         else if(SvOK(gv)){
134             return TRUE;
135         }
136     }
137     return FALSE;
138 }
139
140
141 SV *
142 mouse_call0 (pTHX_ SV *const self, SV *const method)
143 {
144     dSP;
145     SV *ret;
146
147     PUSHMARK(SP);
148     XPUSHs(self);
149     PUTBACK;
150
151     call_sv(method, G_SCALAR | G_METHOD);
152
153     SPAGAIN;
154     ret = POPs;
155     PUTBACK;
156
157     return ret;
158 }
159
160 SV *
161 mouse_call1 (pTHX_ SV *const self, SV *const method, SV* const arg1)
162 {
163     dSP;
164     SV *ret;
165
166     PUSHMARK(SP);
167     EXTEND(SP, 2);
168     PUSHs(self);
169     PUSHs(arg1);
170     PUTBACK;
171
172     call_sv(method, G_SCALAR | G_METHOD);
173
174     SPAGAIN;
175     ret = POPs;
176     PUTBACK;
177
178     return ret;
179 }
180
181 MAGIC*
182 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
183     MAGIC* mg;
184
185     assert(sv != NULL);
186     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
187         if(mg->mg_virtual == vtbl){
188             return mg;
189         }
190     }
191
192     if(flags & MOUSEf_DIE_ON_FAIL){
193         croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
194     }
195     return NULL;
196 }