Use bit-or, not simple assignment
[gitmo/Mouse.git] / xs-src / mouse_util.xs
1 #include "mouse.h"
2
3 #define ISA_CACHE "::LINEALIZED_ISA_CACHE::"
4
5 #ifdef 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 #ifdef DEBUGGING
73 SV**
74 mouse_av_at_safe(pTHX_ AV* const av, I32 const ix){
75     assert(av);
76     assert(SvTYPE(av) == SVt_PVAV);
77     assert(AvMAX(av) >= ix);
78     return &AvARRAY(av)[ix];
79 }
80 #endif
81
82 void
83 mouse_throw_error(SV* const metaobject, SV* const data /* not used */, const char* const fmt, ...){
84     dTHX;
85     va_list args;
86     SV* message;
87
88     PERL_UNUSED_ARG(data); /* for moose-compat */
89
90     assert(metaobject);
91     assert(fmt);
92
93     va_start(args, fmt);
94     message = vnewSVpvf(fmt, &args);
95     va_end(args);
96
97     {
98         dSP;
99         PUSHMARK(SP);
100         EXTEND(SP, 4);
101
102         PUSHs(metaobject);
103         mPUSHs(message);
104
105         mPUSHs(newSVpvs("depth"));
106         mPUSHi(-1);
107
108         PUTBACK;
109
110         call_method("throw_error", G_VOID);
111         croak("throw_error() did not throw the error (%"SVf")", message);
112     }
113 }
114
115
116 /* equivalent to "blessed($x) && $x->isa($klass)" */
117 bool
118 mouse_is_instance_of(pTHX_ SV* const sv, SV* const klass){
119     assert(sv);
120     assert(klass);
121
122     if(IsObject(sv) && SvOK(klass)){
123         bool ok;
124
125         ENTER;
126         SAVETMPS;
127
128         ok = SvTRUEx(mcall1s(sv, "isa", klass));
129
130         FREETMPS;
131         LEAVE;
132
133         return ok;
134     }
135
136     return FALSE;
137 }
138
139
140 bool
141 mouse_is_class_loaded(pTHX_ SV * const klass){
142     HV *stash;
143     GV** gvp;
144     HE* he;
145
146     if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
147         return FALSE;
148     }
149
150     stash = gv_stashsv(klass, FALSE);
151     if (!stash) {
152         return FALSE;
153     }
154
155     if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
156         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
157             return TRUE;
158         }
159     }
160
161     if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
162         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
163             return TRUE;
164         }
165     }
166
167     hv_iterinit(stash);
168     while(( he = hv_iternext(stash) )){
169         GV* const gv = (GV*)HeVAL(he);
170
171         if(isGV(gv)){
172             if(GvCVu(gv)){
173                 return TRUE;
174             }
175         }
176         else if(SvOK(gv)){
177             return TRUE;
178         }
179     }
180     return FALSE;
181 }
182
183
184 SV *
185 mouse_call0 (pTHX_ SV *const self, SV *const method)
186 {
187     dSP;
188     SV *ret;
189
190     PUSHMARK(SP);
191     XPUSHs(self);
192     PUTBACK;
193
194     call_sv(method, G_SCALAR | G_METHOD);
195
196     SPAGAIN;
197     ret = POPs;
198     PUTBACK;
199
200     return ret;
201 }
202
203 SV *
204 mouse_call1 (pTHX_ SV *const self, SV *const method, SV* const arg1)
205 {
206     dSP;
207     SV *ret;
208
209     PUSHMARK(SP);
210     EXTEND(SP, 2);
211     PUSHs(self);
212     PUSHs(arg1);
213     PUTBACK;
214
215     call_sv(method, G_SCALAR | G_METHOD);
216
217     SPAGAIN;
218     ret = POPs;
219     PUTBACK;
220
221     return ret;
222 }
223
224 MAGIC*
225 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
226     MAGIC* mg;
227
228     assert(sv != NULL);
229     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
230         if(mg->mg_virtual == vtbl){
231             return mg;
232         }
233     }
234
235     if(flags & MOUSEf_DIE_ON_FAIL){
236         croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
237     }
238     return NULL;
239 }