Fix RT #54203 (reported by chocolateboy) that setters might return undef.
[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
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
646c0371 121bool
122mouse_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
0aad0266 165SV*
166mouse_call0 (pTHX_ SV* const self, SV* const method) {
646c0371 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
0aad0266 183SV*
184mouse_call1 (pTHX_ SV* const self, SV* const method, SV* const arg1) {
646c0371 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
0aad0266 203int
204mouse_predicate_call(pTHX_ SV* const self, SV* const method) {
6ad77996 205 return sv_true( mcall0(self, method) );
0aad0266 206}
207
a39e9541 208SV*
aa2d2e2c 209mouse_get_metaclass(pTHX_ SV* metaclass_name){
fd168725 210 dMY_CXT;
211 HE* he;
a39e9541 212
aa2d2e2c 213 assert(metaclass_name);
fd168725 214 assert(MY_CXT.metas);
215
aa2d2e2c 216 if(IsObject(metaclass_name)){
a5c683f6 217 HV* const stash = SvSTASH(SvRV(metaclass_name));
aa2d2e2c 218
219 metaclass_name = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
220 sv_2mortal(metaclass_name);
221 }
222
fd168725 223 he = hv_fetch_ent(MY_CXT.metas, metaclass_name, FALSE, 0U);
a39e9541 224
fd168725 225 return he ? HeVAL(he) : &PL_sv_undef;
a39e9541 226}
227
646c0371 228MAGIC*
229mouse_mg_find(pTHX_ SV* const sv, const MGVTBL* const vtbl, I32 const flags){
230 MAGIC* mg;
231
232 assert(sv != NULL);
233 for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic){
234 if(mg->mg_virtual == vtbl){
235 return mg;
236 }
237 }
238
239 if(flags & MOUSEf_DIE_ON_FAIL){
240 croak("mouse_mg_find: no MAGIC found for %"SVf, sv_2mortal(newRV_inc(sv)));
241 }
242 return NULL;
243}
244
a39e9541 245GV*
246mouse_stash_fetch(pTHX_ HV* const stash, const char* const name, I32 const namelen, I32 const create) {
247 GV** const gvp = (GV**)hv_fetch(stash, name, namelen, create);
248
249 if(gvp){
250 if(!isGV(*gvp)){
251 gv_init(*gvp, stash, name, namelen, GV_ADDMULTI);
252 }
253 return *gvp;
254 }
255 else{
256 return NULL;
257 }
258}
259
646c0371 260MODULE = Mouse::Util PACKAGE = Mouse::Util
261
262PROTOTYPES: DISABLE
263VERSIONCHECK: DISABLE
264
fd168725 265BOOT:
266{
267 MY_CXT_INIT;
268 MY_CXT.metas = NULL;
269}
270
271void
272__register_metaclass_storage(HV* metas, bool cloning)
273CODE:
274{
275 if(cloning){
276 MY_CXT_CLONE;
277 MY_CXT.metas = NULL;
278 }
279 {
280 dMY_CXT;
281 if(MY_CXT.metas) croak("Cannot set metaclass storage more than once");
282 MY_CXT.metas = metas;
283 SvREFCNT_inc_simple_void_NN(metas);
284 }
285}
286
646c0371 287bool
288is_class_loaded(SV* sv)
289
290void
291get_code_info(CV* code)
292PREINIT:
293 GV* gv;
294 HV* stash;
295PPCODE:
296 if((gv = CvGV(code)) && isGV(gv) && (stash = GvSTASH(gv))){
297 EXTEND(SP, 2);
298 mPUSHs(newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U));
299 mPUSHs(newSVpvn_share(GvNAME_get(gv), GvNAMELEN_get(gv), 0U));
300 }
301
302SV*
303get_code_package(CV* code)
304PREINIT:
305 HV* stash;
306CODE:
307 if(CvGV(code) && isGV(CvGV(code)) && (stash = GvSTASH(CvGV(code)))){
308 RETVAL = newSVpvn_share(HvNAME_get(stash), HvNAMELEN_get(stash), 0U);
309 }
310 else{
311 RETVAL = &PL_sv_no;
312 }
313OUTPUT:
314 RETVAL
315
316CV*
317get_code_ref(SV* package, SV* name)
318CODE:
319{
320 HV* stash;
a39e9541 321 STRLEN name_len;
322 const char* name_pv;
323 GV* gv;
646c0371 324
325 if(!SvOK(package)){
326 croak("You must define a package name");
327 }
328 if(!SvOK(name)){
329 croak("You must define a subroutine name");
330 }
331
332 stash = gv_stashsv(package, FALSE);
333 if(!stash){
334 XSRETURN_UNDEF;
335 }
a39e9541 336
337 name_pv = SvPV_const(name, name_len);
338 gv = stash_fetch(stash, name_pv, name_len, FALSE);
339 RETVAL = gv ? GvCVu(gv) : NULL;
646c0371 340
341 if(!RETVAL){
342 XSRETURN_UNDEF;
343 }
344}
345OUTPUT:
346 RETVAL
347
348void
ebe91068 349generate_isa_predicate_for(SV* arg, SV* predicate_name = NULL)
350ALIAS:
351 generate_isa_predicate_for = 0
352 generate_can_predicate_for = 1
646c0371 353PPCODE:
354{
441964ce 355 const char* name_pv = NULL;
646c0371 356 CV* xsub;
357
ebe91068 358 SvGETMAGIC(arg);
441964ce 359
ebe91068 360 if(!SvOK(arg)){
361 croak("You must define %s", ix == 0 ? "a class name" : "method names");
646c0371 362 }
646c0371 363
441964ce 364 if(predicate_name){
365 SvGETMAGIC(predicate_name);
366 if(!SvOK(predicate_name)){
ebe91068 367 croak("You must define %s", "a predicate name");
441964ce 368 }
369 name_pv = SvPV_nolen_const(predicate_name);
646c0371 370 }
371
ebe91068 372 if(ix == 0){
373 xsub = mouse_generate_isa_predicate_for(aTHX_ arg, name_pv);
374 }
375 else{
376 xsub = mouse_generate_can_predicate_for(aTHX_ arg, name_pv);
377 }
441964ce 378
646c0371 379 if(predicate_name == NULL){ /* anonymous predicate */
380 XPUSHs( newRV_noinc((SV*)xsub) );
381 }
382}