Mouse::Util::does_role() respects $thing->does() method
[gitmo/Mouse.git] / xs-src / MouseUtil.xs
CommitLineData
646c0371 1#include "mouse.h"
2
fd168725 3#define MY_CXT_KEY "Mouse::Util::_guts" XS_VERSION
4typedef struct {
5 HV* metas;
6} my_cxt_t;
7START_MY_CXT
8
646c0371 9#define ISA_CACHE "::LINEALIZED_ISA_CACHE::"
10
11#ifdef no_mro_get_linear_isa
12AV*
13mouse_mro_get_linear_isa(pTHX_ HV* const stash){
6582d0e3 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{
cd658d1f 30 SvREFCNT_dec(isa);
31 GvAV(cachegv) = isa = newAV();
6582d0e3 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
80aa5731 53 if(IsArrayRef(avref)){
6582d0e3 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));
cd658d1f 74 return isa;
646c0371 75}
76#endif /* !no_mor_get_linear_isa */
77
78#ifdef DEBUGGING
79SV**
80mouse_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
88void
89mouse_throw_error(SV* const metaobject, SV* const data /* not used */, const char* const fmt, ...){
90 dTHX;
91 va_list args;
92 SV* message;
93
646c0371 94 assert(metaobject);
95 assert(fmt);
96
97 va_start(args, fmt);
98 message = vnewSVpvf(fmt, &args);
99 va_end(args);
100
101 {
102 dSP;
103 PUSHMARK(SP);
077f2efd 104 EXTEND(SP, 6);
646c0371 105
106 PUSHs(metaobject);
107 mPUSHs(message);
108
431e4817 109 if(data){ /* extra arg, might be useful for debugging */
077f2efd 110 mPUSHs(newSVpvs("data"));
431e4817 111 PUSHs(data);
112 mPUSHs(newSVpvs("depth"));
113 mPUSHi(-1);
114 }
646c0371 115 PUTBACK;
dcce2592 116 if(SvOK(metaobject)) {
117 call_method("throw_error", G_VOID);
118 }
119 else {
120 call_pv("Mouse::Util::throw_error", G_VOID);
121 }
646c0371 122 croak("throw_error() did not throw the error (%"SVf")", message);
123 }
124}
125
8fcdb997 126/* workaround Perl-RT #69939 */
b3cd4c14 127I32
128mouse_call_sv_safe(pTHX_ SV* const sv, I32 const flags) {
f0e1969b 129 I32 count;
130 ENTER;
131 /* Don't do SAVETMPS */
b3cd4c14 132
f0e1969b 133 SAVESPTR(ERRSV);
134 ERRSV = sv_newmortal();
b3cd4c14 135
f0e1969b 136 count = Perl_call_sv(aTHX_ sv, flags | G_EVAL);
8fcdb997 137
f0e1969b 138 if(sv_true(ERRSV)){
139 SV* const err = sv_mortalcopy(ERRSV);
8fcdb997 140 LEAVE;
f0e1969b 141 sv_setsv(ERRSV, err);
142 croak(NULL); /* rethrow */
b3cd4c14 143 }
f0e1969b 144
145 LEAVE;
146
147 return count;
b3cd4c14 148}
149
d06d9266 150void
151mouse_must_defined(pTHX_ SV* const value, const char* const name) {
152 assert(value);
153 assert(name);
154
155 SvGETMAGIC(value);
156 if(!SvOK(value)){
157 croak("You must define %s", name);
158 }
159}
160
161void
162mouse_must_ref(pTHX_ SV* const value, const char* const name, svtype const t) {
163 assert(value);
164 assert(name);
165
166 SvGETMAGIC(value);
167 if(!(SvROK(value) && (t == SVt_NULL || SvTYPE(SvRV(value)) == t))) {
168 croak("You must pass %s, not %s",
169 name, SvOK(value) ? SvPV_nolen(value) : "undef");
170 }
171}
172
173
646c0371 174bool
175mouse_is_class_loaded(pTHX_ SV * const klass){
176 HV *stash;
177 GV** gvp;
178 HE* he;
179
180 if (!(SvPOKp(klass) && SvCUR(klass))) { /* XXX: SvPOK does not work with magical scalars */
181 return FALSE;
182 }
183
184 stash = gv_stashsv(klass, FALSE);
185 if (!stash) {
186 return FALSE;
187 }
188
189 if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
190 if(isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp))){
191 return TRUE;
192 }
193 }
194
195 if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
196 if(isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1){
197 return TRUE;
198 }
199 }
200
201 hv_iterinit(stash);
202 while(( he = hv_iternext(stash) )){
203 GV* const gv = (GV*)HeVAL(he);
204
205 if(isGV(gv)){
fa4ac648 206 if(GvCVu(gv)){ /* is GV and has CV */
207 hv_iterinit(stash); /* reset */
646c0371 208 return TRUE;
209 }
210 }
fa4ac648 211 else if(SvOK(gv)){ /* is a stub or constant */
212 hv_iterinit(stash); /* reset */
646c0371 213 return TRUE;
214 }
215 }
216 return FALSE;
217}
218
219
0aad0266 220SV*
221mouse_call0 (pTHX_ SV* const self, SV* const method) {
646c0371 222 dSP;
223 SV *ret;
224
225 PUSHMARK(SP);
226 XPUSHs(self);
227 PUTBACK;
228
b3cd4c14 229 call_sv_safe(method, G_SCALAR | G_METHOD);
646c0371 230
231 SPAGAIN;
232 ret = POPs;
233 PUTBACK;
234
235 return ret;
236}
237
0aad0266 238SV*
239mouse_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
646c0371 240 dSP;
241 SV *ret;
242
243 PUSHMARK(SP);
244 EXTEND(SP, 2);
245 PUSHs(self);
246 PUSHs(arg1);
247 PUTBACK;
248
b3cd4c14 249 call_sv_safe(method, G_SCALAR | G_METHOD);
646c0371 250
251 SPAGAIN;
252 ret = POPs;
253 PUTBACK;
254
255 return ret;
256}
257
0aad0266 258int
259mouse_predicate_call(pTHX_ SV* const self, SV* const method) {
6ad77996 260 return sv_true( mcall0(self, method) );
0aad0266 261}
262
a39e9541 263SV*
aa2d2e2c 264mouse_get_metaclass(pTHX_ SV* metaclass_name){
fd168725 265 dMY_CXT;
266 HE* he;
a39e9541 267
aa2d2e2c 268 assert(metaclass_name);
fd168725 269 assert(MY_CXT.metas);
270
aa2d2e2c 271 if(IsObject(metaclass_name)){
a5c683f6 272 HV* const stash = SvSTASH(SvRV(metaclass_name));
aa2d2e2c 273
274 metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
275 sv_2mortal(metaclass_name);
276 }
277
fd168725 278 he = hv_fetch_ent(MY_CXT.metas, metaclass_name, FALSE, 0U);
a39e9541 279
fd168725 280 return he ? HeVAL(he) : &PL_sv_undef;
a39e9541 281}
282
646c0371 283MAGIC*
284mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
285 MAGIC* mg;
286
287 assert(sv != NULL);
288 for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
289 if(mg->mg_virtual == vtbl){
290 return mg;
291 }
292 }
293
294 if(flags & MOUSEf_DIE_ON_FAIL){
295 croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
296 }
297 return NULL;
298}
299
a39e9541 300GV*
301mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
302 GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
303
304 if(gvp){
305 if(!isGV(*gvp)){
306 gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
307 }
308 return *gvp;
309 }
310 else{
311 return NULL;
312 }
313}
314
d67f600d 315void
316mouse_install_sub(pTHX_ GV* const gv, SV* const code_ref) {
317 CV* cv;
318
319 assert(gv != NULL);
320 assert(code_ref != NULL);
321 assert(isGV(gv));
322 assert(IsCodeRef(code_ref));
323
324 if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */
325 SvREFCNT_dec(GvCV(gv));
952a7aee 326 GvCV_set(gv, NULL);
d67f600d 327 }
d06d9266 328
d67f600d 329 sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */
330
331 /* name the CODE ref if it's anonymous */
332 cv = (CV*)SvRV(code_ref);
333 if(CvANON(cv)
334 && CvGV(cv) /* a cv under construction has no gv */ ){
335 HV* dbsub;
336
337 /* update %DB::sub to make NYTProf happy */
338 if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON))
339 && PL_DBsub && (dbsub = GvHV(PL_DBsub))
340 ){
341 /* see Perl_newATTRSUB() in op.c */
342 SV* const subname = sv_newmortal();
343 HE* orig;
344
345 gv_efullname3(subname, CvGV(cv), NULL);
346 orig = hv_fetch_ent(dbsub, subname, FALSE, 0U);
347 if(orig){
348 gv_efullname3(subname, gv, NULL);
349 (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U);
350 SvREFCNT_inc_simple_void_NN(HeVAL(orig));
351 }
352 }
353
d6ceb359 354 CvGV_set(cv, gv);
d67f600d 355 CvANON_off(cv);
356 }
357}
358
646c0371 359MODULE = Mouse::Util PACKAGE = Mouse::Util
360
361PROTOTYPES: DISABLE
362VERSIONCHECK: DISABLE
363
fd168725 364BOOT:
365{
366 MY_CXT_INIT;
367 MY_CXT.metas = NULL;
368}
369
370void
371__register_metaclass_storage(HV* metas, bool cloning)
372CODE:
373{
374 if(cloning){
375 MY_CXT_CLONE;
376 MY_CXT.metas = NULL;
377 }
378 {
379 dMY_CXT;
25ba356e 380 if(MY_CXT.metas && ckWARN(WARN_REDEFINE)){
381 Perl_warner(aTHX_ packWARN(WARN_REDEFINE), "Metaclass storage more than once");
382 }
fd168725 383 MY_CXT.metas = metas;
384 SvREFCNT_inc_simple_void_NN(metas);
385 }
386}
387
646c0371 388bool
0ffc4183 389is_valid_class_name(SV* sv)
390CODE:
391{
392 SvGETMAGIC(sv);
393 if(SvPOKp(sv) && SvCUR(sv) > 0){
394 UV i;
395 RETVAL = TRUE;
396 for(i = 0; i < SvCUR(sv); i++){
397 char const c = SvPVX(sv)[i];
398 if(!(isALNUM(c) || c == ':')){
399 RETVAL = FALSE;
400 break;
401 }
402 }
403 }
404 else{
405 RETVAL = SvNIOKp(sv) ? TRUE : FALSE;
406 }
407}
408OUTPUT:
409 RETVAL
410
411bool
646c0371 412is_class_loaded(SV* sv)
413
414void
415get_code_info(CV* code)
416PREINIT:
417 GV* gv;
418 HV* stash;
419PPCODE:
420 if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
421 EXTEND(SP, 2);
422 mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
423 mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
424 }
425
426SV*
427get_code_package(CV* code)
428PREINIT:
429 HV* stash;
430CODE:
431 if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
432 RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
433 }
434 else{
435 RETVAL = &PL_sv_no;
436 }
437OUTPUT:
438 RETVAL
439
440CV*
441get_code_ref(SV* package, SV* name)
442CODE:
443{
444 HV* stash;
a39e9541 445 STRLEN name_len;
446 const char* name_pv;
447 GV* gv;
646c0371 448
d06d9266 449 must_defined(package, "a package name");
450 must_defined(name, "a subroutine name");
646c0371 451
452 stash = gv_stashsv(package, FALSE);
453 if(!stash){
454 XSRETURN_UNDEF;
455 }
a39e9541 456
457 name_pv = SvPV_const(name, name_len);
458 gv = stash_fetch(stash, name_pv, name_len, FALSE);
459 RETVAL = gv ? GvCVu(gv) : NULL;
646c0371 460
461 if(!RETVAL){
462 XSRETURN_UNDEF;
463 }
464}
465OUTPUT:
466 RETVAL
467
468void
ebe91068 469generate_isa_predicate_for(SV* arg, SV* predicate_name = NULL)
470ALIAS:
471 generate_isa_predicate_for = 0
472 generate_can_predicate_for = 1
646c0371 473PPCODE:
474{
441964ce 475 const char* name_pv = NULL;
646c0371 476 CV* xsub;
477
d06d9266 478 must_defined(arg, ix == 0 ? "a class_name" : "method names");
646c0371 479
441964ce 480 if(predicate_name){
d06d9266 481 must_defined(predicate_name, "a predicate name");
441964ce 482 name_pv = SvPV_nolen_const(predicate_name);
646c0371 483 }
484
ebe91068 485 if(ix == 0){
486 xsub = mouse_generate_isa_predicate_for(aTHX_ arg, name_pv);
487 }
488 else{
489 xsub = mouse_generate_can_predicate_for(aTHX_ arg, name_pv);
490 }
441964ce 491
646c0371 492 if(predicate_name == NULL){ /* anonymous predicate */
73337709 493 mXPUSHs( newRV_inc((SV*)xsub) );
646c0371 494 }
495}
d67f600d 496
497# This xsub will redefine &Mouse::Util::install_subroutines()
498void
499install_subroutines(SV* into, ...)
500CODE:
501{
502 HV* stash;
503 I32 i;
504
d06d9266 505 must_defined(into, "a package name");
d67f600d 506 stash = gv_stashsv(into, TRUE);
507
508 if( ((items-1) % 2) != 0 ){
509 croak_xs_usage(cv, "into, name => coderef [, other_name, other_coderef ...]");
510 }
511
512 for(i = 1; i < items; i += 2) {
513 SV* const name = ST(i);
514 SV* const code = ST(i+1);
515 STRLEN len;
516 const char* pv;
517 GV* gv;
518
d06d9266 519 must_defined(name, "a subroutine name");
520 must_ref(code, "a CODE reference", SVt_PVCV);
d67f600d 521
522 pv = SvPV_const(name, len);
523 gv = stash_fetch(stash, pv, len, TRUE);
524
525 mouse_install_sub(aTHX_ gv, code);
526 }
527}