More robust tests for threads
[gitmo/Mouse.git] / xs-src / MouseUtil.xs
1 #include "mouse.h"
2
3 #define MY_CXT_KEY "Mouse::Util::_guts" XS_VERSION
4 typedef struct {
5     HV* metas;
6 } my_cxt_t;
7 START_MY_CXT
8
9 #define ISA_CACHE "::LINEALIZED_ISA_CACHE::"
10
11 #ifdef no_mro_get_linear_isa
12 AV*
13 mouse_mro_get_linear_isa(pTHX_ HV* const stash){
14     GV* const cachegv = *(GV**)hv_fetchs(stash, ISA_CACHE, TRUE);
15     AV* isa;
16     SV* gen;
17     CV* get_linear_isa;
18
19     if(!isGV(cachegv))
20         gv_init(cachegv, stash, ISA_CACHE, sizeof(ISA_CACHE)-1, TRUE);
21
22     isa = GvAVn(cachegv);
23     gen = GvSVn(cachegv);
24
25
26     if(SvIOK(gen) && SvIVX(gen) == (IV)mro_get_pkg_gen(stash)){
27         return isa; /* returns the cache if available */
28     }
29     else{
30         SvREFCNT_dec(isa);
31         GvAV(cachegv) = isa = newAV();
32     }
33
34     get_linear_isa = get_cv("Mouse::Util::get_linear_isa", TRUE);
35
36     {
37         SV* avref;
38         dSP;
39
40         ENTER;
41         SAVETMPS;
42
43         PUSHMARK(SP);
44         mXPUSHp(HvNAME_get(stash), HvNAMELEN_get(stash));
45         PUTBACK;
46
47         call_sv((SV*)get_linear_isa, G_SCALAR);
48
49         SPAGAIN;
50         avref = POPs;
51         PUTBACK;
52
53         if(IsArrayRef(avref)){
54             AV* const av  = (AV*)SvRV(avref);
55             I32 const len = AvFILLp(av) + 1;
56             I32 i;
57
58             for(i = 0; i < len; i++){
59                 HV* const stash = gv_stashsv(AvARRAY(av)[i], FALSE);
60                 if(stash)
61                     av_push(isa, newSVpv(HvNAME(stash), 0));
62             }
63             SvREADONLY_on(isa);
64         }
65         else{
66             Perl_croak(aTHX_ "Mouse:Util::get_linear_isa() didn't return an ARRAY reference");
67         }
68
69         FREETMPS;
70         LEAVE;
71     }
72
73     sv_setiv(gen, (IV)mro_get_pkg_gen(stash));
74     return isa;
75 }
76 #endif /* !no_mor_get_linear_isa */
77
78 #ifdef DEBUGGING
79 SV**
80 mouse_av_at_safe(pTHX_ AV* const av, I32 const ix){
81     assert(av);
82     assert(SvTYPE(av) == SVt_PVAV);
83     assert(AvMAX(av) >= ix);
84     return &AvARRAY(av)[ix];
85 }
86 #endif
87
88 void
89 mouse_throw_error(SV* const metaobject, SV* const data /* not used */, const char* const fmt, ...){
90     dTHX;
91     va_list args;
92     SV* message;
93
94     PERL_UNUSED_ARG(data); /* for moose-compat */
95
96     assert(metaobject);
97     assert(fmt);
98
99     va_start(args, fmt);
100     message = vnewSVpvf(fmt, &args);
101     va_end(args);
102
103     {
104         dSP;
105         PUSHMARK(SP);
106         EXTEND(SP, 4);
107
108         PUSHs(metaobject);
109         mPUSHs(message);
110
111         mPUSHs(newSVpvs("depth"));
112         mPUSHi(-1);
113
114         PUTBACK;
115
116         call_method("throw_error", G_VOID);
117         croak("throw_error() did not throw the error (%"SVf")", message);
118     }
119 }
120
121 bool
122 mouse_is_class_loaded(pTHX_ SV * const klass){
123     HV *stash;
124     GV** gvp;
125     HE* he;
126
127     if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
128         return FALSE;
129     }
130
131     stash = gv_stashsv(klass, FALSE);
132     if (!stash) {
133         return FALSE;
134     }
135
136     if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
137         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
138             return TRUE;
139         }
140     }
141
142     if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
143         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
144             return TRUE;
145         }
146     }
147
148     hv_iterinit(stash);
149     while(( he = hv_iternext(stash) )){
150         GV* const gv = (GV*)HeVAL(he);
151
152         if(isGV(gv)){
153             if(GvCVu(gv)){
154                 return TRUE;
155             }
156         }
157         else if(SvOK(gv)){
158             return TRUE;
159         }
160     }
161     return FALSE;
162 }
163
164
165 SV*
166 mouse_call0 (pTHX_ SV* const self, SV* const method) {
167     dSP;
168     SV *ret;
169
170     PUSHMARK(SP);
171     XPUSHs(self);
172     PUTBACK;
173
174     call_sv(method, G_SCALAR | G_METHOD);
175
176     SPAGAIN;
177     ret = POPs;
178     PUTBACK;
179
180     return ret;
181 }
182
183 SV*
184 mouse_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
185     dSP;
186     SV *ret;
187
188     PUSHMARK(SP);
189     EXTEND(SP, 2);
190     PUSHs(self);
191     PUSHs(arg1);
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 int
204 mouse_predicate_call(pTHX_ SV* const self, SV* const method) {
205     SV* const value = mcall0(self, method);
206     return SvTRUE(value);
207 }
208
209 SV*
210 mouse_get_metaclass(pTHX_ SV* metaclass_name){
211     dMY_CXT;
212     HE* he;
213
214     assert(metaclass_name);
215     assert(MY_CXT.metas);
216
217     if(IsObject(metaclass_name)){
218         HV* const stash = SvSTASH(SvRV(metaclass_name));
219
220         metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
221         sv_2mortal(metaclass_name);
222     }
223
224     he = hv_fetch_ent(MY_CXT.metas, metaclass_name, FALSE, 0U);
225
226     return he ? HeVAL(he) : &PL_sv_undef;
227 }
228
229 MAGIC*
230 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
231     MAGIC* mg;
232
233     assert(sv != NULL);
234     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
235         if(mg->mg_virtual == vtbl){
236             return mg;
237         }
238     }
239
240     if(flags & MOUSEf_DIE_ON_FAIL){
241         croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
242     }
243     return NULL;
244 }
245
246 GV*
247 mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
248     GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
249
250     if(gvp){
251         if(!isGV(*gvp)){
252             gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
253         }
254         return *gvp;
255     }
256     else{
257         return NULL;
258     }
259 }
260
261 MODULE = Mouse::Util  PACKAGE = Mouse::Util
262
263 PROTOTYPES:   DISABLE
264 VERSIONCHECK: DISABLE
265
266 BOOT:
267 {
268     MY_CXT_INIT;
269     MY_CXT.metas = NULL;
270 }
271
272 void
273 __register_metaclass_storage(HV* metas, bool cloning)
274 CODE:
275 {
276     if(cloning){
277         MY_CXT_CLONE;
278         MY_CXT.metas = NULL;
279     }
280     {
281         dMY_CXT;
282         if(MY_CXT.metas) croak("Cannot set metaclass storage more than once");
283         MY_CXT.metas = metas;
284         SvREFCNT_inc_simple_void_NN(metas);
285     }
286 }
287
288 bool
289 is_class_loaded(SV* sv)
290
291 void
292 get_code_info(CV* code)
293 PREINIT:
294     GV* gv;
295     HV* stash;
296 PPCODE:
297     if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
298         EXTEND(SP, 2);
299         mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
300         mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
301     }
302
303 SV*
304 get_code_package(CV* code)
305 PREINIT:
306     HV* stash;
307 CODE:
308     if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
309         RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
310     }
311     else{
312         RETVAL = &PL_sv_no;
313     }
314 OUTPUT:
315     RETVAL
316
317 CV*
318 get_code_ref(SV* package, SV* name)
319 CODE:
320 {
321     HV* stash;
322     STRLEN name_len;
323     const char* name_pv;
324     GV* gv;
325
326     if(!SvOK(package)){
327         croak("You must define a package name");
328     }
329     if(!SvOK(name)){
330         croak("You must define a subroutine name");
331     }
332
333     stash = gv_stashsv(package, FALSE);
334     if(!stash){
335         XSRETURN_UNDEF;
336     }
337
338     name_pv = SvPV_const(name, name_len);
339     gv = stash_fetch(stash, name_pv, name_len, FALSE);
340     RETVAL = gv ? GvCVu(gv) : NULL;
341
342     if(!RETVAL){
343         XSRETURN_UNDEF;
344     }
345 }
346 OUTPUT:
347     RETVAL
348
349 void
350 generate_isa_predicate_for(SV* klass, SV* predicate_name = NULL)
351 PPCODE:
352 {
353     const char* name_pv = NULL;
354     CV* xsub;
355
356     SvGETMAGIC(klass);
357
358     if(!SvOK(klass)){
359         croak("You must define a class name");
360     }
361
362     if(predicate_name){
363         SvGETMAGIC(predicate_name);
364         if(!SvOK(predicate_name)){
365             croak("You must define a predicate_name");
366         }
367         name_pv = SvPV_nolen_const(predicate_name);
368     }
369
370     xsub = mouse_generate_isa_predicate_for(aTHX_ klass, name_pv);
371
372     if(predicate_name == NULL){ /* anonymous predicate */
373         XPUSHs( newRV_noinc((SV*)xsub) );
374     }
375 }