No -T in tests
[gitmo/Mouse.git] / xs-src / MouseUtil.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         SvREFCNT_dec(isa);
25         GvAV(cachegv) = isa = newAV();
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(IsArrayRef(avref)){
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 isa;
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 bool
116 mouse_is_class_loaded(pTHX_ SV * const klass){
117     HV *stash;
118     GV** gvp;
119     HE* he;
120
121     if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
122         return FALSE;
123     }
124
125     stash = gv_stashsv(klass, FALSE);
126     if (!stash) {
127         return FALSE;
128     }
129
130     if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
131         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
132             return TRUE;
133         }
134     }
135
136     if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
137         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
138             return TRUE;
139         }
140     }
141
142     hv_iterinit(stash);
143     while(( he = hv_iternext(stash) )){
144         GV* const gv = (GV*)HeVAL(he);
145
146         if(isGV(gv)){
147             if(GvCVu(gv)){
148                 return TRUE;
149             }
150         }
151         else if(SvOK(gv)){
152             return TRUE;
153         }
154     }
155     return FALSE;
156 }
157
158
159 SV*
160 mouse_call0 (pTHX_ SV* const self, SV* const method) {
161     dSP;
162     SV *ret;
163
164     PUSHMARK(SP);
165     XPUSHs(self);
166     PUTBACK;
167
168     call_sv(method, G_SCALAR | G_METHOD);
169
170     SPAGAIN;
171     ret = POPs;
172     PUTBACK;
173
174     return ret;
175 }
176
177 SV*
178 mouse_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
179     dSP;
180     SV *ret;
181
182     PUSHMARK(SP);
183     EXTEND(SP, 2);
184     PUSHs(self);
185     PUSHs(arg1);
186     PUTBACK;
187
188     call_sv(method, G_SCALAR | G_METHOD);
189
190     SPAGAIN;
191     ret = POPs;
192     PUTBACK;
193
194     return ret;
195 }
196
197 int
198 mouse_predicate_call(pTHX_ SV* const self, SV* const method) {
199     SV* const value = mcall0(self, method);
200     return SvTRUE(value);
201 }
202
203 SV*
204 mouse_get_metaclass(pTHX_ SV* metaclass_name){
205     CV* const get_metaclass  = get_cvs("Mouse::Util::get_metaclass_by_name", TRUE);
206     SV* metaclass;
207     dSP;
208
209     assert(metaclass_name);
210     if(IsObject(metaclass_name)){
211         HV* const stash = SvSTASH(metaclass_name);
212
213         metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
214         sv_2mortal(metaclass_name);
215     }
216
217     PUSHMARK(SP);
218     XPUSHs(metaclass_name);
219     PUTBACK;
220
221     call_sv((SV*)get_metaclass, G_SCALAR);
222
223     SPAGAIN;
224     metaclass = POPs;
225     PUTBACK;
226
227     return metaclass;
228 }
229
230 MAGIC*
231 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
232     MAGIC* mg;
233
234     assert(sv != NULL);
235     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
236         if(mg->mg_virtual == vtbl){
237             return mg;
238         }
239     }
240
241     if(flags & MOUSEf_DIE_ON_FAIL){
242         croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
243     }
244     return NULL;
245 }
246
247 GV*
248 mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
249     GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
250
251     if(gvp){
252         if(!isGV(*gvp)){
253             gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
254         }
255         return *gvp;
256     }
257     else{
258         return NULL;
259     }
260 }
261
262 MODULE = Mouse::Util  PACKAGE = Mouse::Util
263
264 PROTOTYPES:   DISABLE
265 VERSIONCHECK: DISABLE
266
267 bool
268 is_class_loaded(SV* sv)
269
270 void
271 get_code_info(CV* code)
272 PREINIT:
273     GV* gv;
274     HV* stash;
275 PPCODE:
276     if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
277         EXTEND(SP, 2);
278         mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
279         mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
280     }
281
282 SV*
283 get_code_package(CV* code)
284 PREINIT:
285     HV* stash;
286 CODE:
287     if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
288         RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
289     }
290     else{
291         RETVAL = &PL_sv_no;
292     }
293 OUTPUT:
294     RETVAL
295
296 CV*
297 get_code_ref(SV* package, SV* name)
298 CODE:
299 {
300     HV* stash;
301     STRLEN name_len;
302     const char* name_pv;
303     GV* gv;
304
305     if(!SvOK(package)){
306         croak("You must define a package name");
307     }
308     if(!SvOK(name)){
309         croak("You must define a subroutine name");
310     }
311
312     stash = gv_stashsv(package, FALSE);
313     if(!stash){
314         XSRETURN_UNDEF;
315     }
316
317     name_pv = SvPV_const(name, name_len);
318     gv = stash_fetch(stash, name_pv, name_len, FALSE);
319     RETVAL = gv ? GvCVu(gv) : NULL;
320
321     if(!RETVAL){
322         XSRETURN_UNDEF;
323     }
324 }
325 OUTPUT:
326     RETVAL
327
328 void
329 generate_isa_predicate_for(SV* klass, SV* predicate_name = NULL)
330 PPCODE:
331 {
332     const char* name_pv = NULL;
333     CV* xsub;
334
335     SvGETMAGIC(klass);
336
337     if(!SvOK(klass)){
338         croak("You must define a class name");
339     }
340
341     if(predicate_name){
342         SvGETMAGIC(predicate_name);
343         if(!SvOK(predicate_name)){
344             croak("You must define a predicate_name");
345         }
346         name_pv = SvPV_nolen_const(predicate_name);
347     }
348
349     xsub = mouse_generate_isa_predicate_for(aTHX_ klass, name_pv);
350
351     if(predicate_name == NULL){ /* anonymous predicate */
352         XPUSHs( newRV_noinc((SV*)xsub) );
353     }
354 }