d8e3f4cf5f59fdd128c3cc74ae2e6364696906e2
[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         if(data){ /* extra arg, might be useful for debugging */
112             mPUSHs(newSVpsv("data"));
113             PUSHs(data);
114             mPUSHs(newSVpvs("depth"));
115             mPUSHi(-1);
116         }
117
118         PUTBACK;
119
120         call_method("throw_error", G_VOID);
121         croak("throw_error() did not throw the error (%"SVf")", message);
122     }
123 }
124
125 bool
126 mouse_is_class_loaded(pTHX_ SV * const klass){
127     HV *stash;
128     GV** gvp;
129     HE* he;
130
131     if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
132         return FALSE;
133     }
134
135     stash = gv_stashsv(klass, FALSE);
136     if (!stash) {
137         return FALSE;
138     }
139
140     if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
141         if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
142             return TRUE;
143         }
144     }
145
146     if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
147         if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
148             return TRUE;
149         }
150     }
151
152     hv_iterinit(stash);
153     while(( he = hv_iternext(stash) )){
154         GV* const gv = (GV*)HeVAL(he);
155
156         if(isGV(gv)){
157             if(GvCVu(gv)){
158                 return TRUE;
159             }
160         }
161         else if(SvOK(gv)){
162             return TRUE;
163         }
164     }
165     return FALSE;
166 }
167
168
169 SV*
170 mouse_call0 (pTHX_ SV* const self, SV* const method) {
171     dSP;
172     SV *ret;
173
174     PUSHMARK(SP);
175     XPUSHs(self);
176     PUTBACK;
177
178     call_sv(method, G_SCALAR | G_METHOD);
179
180     SPAGAIN;
181     ret = POPs;
182     PUTBACK;
183
184     return ret;
185 }
186
187 SV*
188 mouse_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
189     dSP;
190     SV *ret;
191
192     PUSHMARK(SP);
193     EXTEND(SP, 2);
194     PUSHs(self);
195     PUSHs(arg1);
196     PUTBACK;
197
198     call_sv(method, G_SCALAR | G_METHOD);
199
200     SPAGAIN;
201     ret = POPs;
202     PUTBACK;
203
204     return ret;
205 }
206
207 int
208 mouse_predicate_call(pTHX_ SV* const self, SV* const method) {
209     return sv_true( mcall0(self, method) );
210 }
211
212 SV*
213 mouse_get_metaclass(pTHX_ SV* metaclass_name){
214     dMY_CXT;
215     HE* he;
216
217     assert(metaclass_name);
218     assert(MY_CXT.metas);
219
220     if(IsObject(metaclass_name)){
221         HV* const stash = SvSTASH(SvRV(metaclass_name));
222
223         metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
224         sv_2mortal(metaclass_name);
225     }
226
227     he = hv_fetch_ent(MY_CXT.metas, metaclass_name, FALSE, 0U);
228
229     return he ? HeVAL(he) : &PL_sv_undef;
230 }
231
232 MAGIC*
233 mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
234     MAGIC* mg;
235
236     assert(sv != NULL);
237     for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
238         if(mg->mg_virtual == vtbl){
239             return mg;
240         }
241     }
242
243     if(flags & MOUSEf_DIE_ON_FAIL){
244         croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
245     }
246     return NULL;
247 }
248
249 GV*
250 mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
251     GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
252
253     if(gvp){
254         if(!isGV(*gvp)){
255             gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
256         }
257         return *gvp;
258     }
259     else{
260         return NULL;
261     }
262 }
263
264 MODULE = Mouse::Util  PACKAGE = Mouse::Util
265
266 PROTOTYPES:   DISABLE
267 VERSIONCHECK: DISABLE
268
269 BOOT:
270 {
271     MY_CXT_INIT;
272     MY_CXT.metas = NULL;
273 }
274
275 void
276 __register_metaclass_storage(HV* metas, bool cloning)
277 CODE:
278 {
279     if(cloning){
280         MY_CXT_CLONE;
281         MY_CXT.metas = NULL;
282     }
283     {
284         dMY_CXT;
285         if(MY_CXT.metas) croak("Cannot set metaclass storage more than once");
286         MY_CXT.metas = metas;
287         SvREFCNT_inc_simple_void_NN(metas);
288     }
289 }
290
291 bool
292 is_valid_class_name(SV* sv)
293 CODE:
294 {
295     SvGETMAGIC(sv);
296     if(SvPOKp(sv) && SvCUR(sv) > 0){
297         UV i;
298         RETVAL = TRUE;
299         for(i = 0; i < SvCUR(sv); i++){
300             char const c = SvPVX(sv)[i];
301             if(!(isALNUM(c) || c == ':')){
302                 RETVAL = FALSE;
303                 break;
304             }
305         }
306     }
307     else{
308         RETVAL = SvNIOKp(sv) ? TRUE : FALSE;
309     }
310 }
311 OUTPUT:
312     RETVAL
313
314 bool
315 is_class_loaded(SV* sv)
316
317 void
318 get_code_info(CV* code)
319 PREINIT:
320     GV* gv;
321     HV* stash;
322 PPCODE:
323     if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
324         EXTEND(SP, 2);
325         mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
326         mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
327     }
328
329 SV*
330 get_code_package(CV* code)
331 PREINIT:
332     HV* stash;
333 CODE:
334     if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
335         RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
336     }
337     else{
338         RETVAL = &PL_sv_no;
339     }
340 OUTPUT:
341     RETVAL
342
343 CV*
344 get_code_ref(SV* package, SV* name)
345 CODE:
346 {
347     HV* stash;
348     STRLEN name_len;
349     const char* name_pv;
350     GV* gv;
351
352     if(!SvOK(package)){
353         croak("You must define a package name");
354     }
355     if(!SvOK(name)){
356         croak("You must define a subroutine name");
357     }
358
359     stash = gv_stashsv(package, FALSE);
360     if(!stash){
361         XSRETURN_UNDEF;
362     }
363
364     name_pv = SvPV_const(name, name_len);
365     gv = stash_fetch(stash, name_pv, name_len, FALSE);
366     RETVAL = gv ? GvCVu(gv) : NULL;
367
368     if(!RETVAL){
369         XSRETURN_UNDEF;
370     }
371 }
372 OUTPUT:
373     RETVAL
374
375 void
376 generate_isa_predicate_for(SV* arg, SV* predicate_name = NULL)
377 ALIAS:
378     generate_isa_predicate_for = 0
379     generate_can_predicate_for = 1
380 PPCODE:
381 {
382     const char* name_pv = NULL;
383     CV* xsub;
384
385     SvGETMAGIC(arg);
386
387     if(!SvOK(arg)){
388         croak("You must define %s", ix == 0 ? "a class name" : "method names");
389     }
390
391     if(predicate_name){
392         SvGETMAGIC(predicate_name);
393         if(!SvOK(predicate_name)){
394             croak("You must define %s", "a predicate name");
395         }
396         name_pv = SvPV_nolen_const(predicate_name);
397     }
398
399     if(ix == 0){
400         xsub = mouse_generate_isa_predicate_for(aTHX_ arg, name_pv);
401     }
402     else{
403         xsub = mouse_generate_can_predicate_for(aTHX_ arg, name_pv);
404     }
405
406     if(predicate_name == NULL){ /* anonymous predicate */
407         mXPUSHs( newRV_inc((SV*)xsub) );
408     }
409 }