Mouse::Util::does_role() respects $thing->does() method
[gitmo/Mouse.git] / xs-src / Mouse.xs
CommitLineData
1b812057 1#define NEED_newSVpvn_flags_GLOBAL
df6dd016 2#include "mouse.h"
3
d39ac61c 4/* keywords for methods/keys */
cccb83de 5SV* mouse_package;
6SV* mouse_namespace;
3e44140b 7SV* mouse_methods;
a5df48e5 8SV* mouse_name;
047d7af0 9SV* mouse_get_attribute;
10SV* mouse_get_attribute_list;
d39ac61c 11SV* mouse_coerce;
cccb83de 12
4e7e3250 13#define MOUSE_xc_flags(a) SvUVX(MOUSE_av_at((a), MOUSE_XC_FLAGS))
a39e9541 14#define MOUSE_xc_gen(a) MOUSE_av_at((a), MOUSE_XC_GEN)
aa2d2e2c 15#define MOUSE_xc_stash(a) ( (HV*)MOUSE_av_at((a), MOUSE_XC_STASH) )
a39e9541 16#define MOUSE_xc_attrall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) )
17#define MOUSE_xc_buildall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) )
9974d611 18#define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEMOLISHALL) )
a39e9541 19
aa2d2e2c 20enum mouse_xc_flags_t {
21 MOUSEf_XC_IS_IMMUTABLE = 0x0001,
22 MOUSEf_XC_IS_ANON = 0x0002,
23 MOUSEf_XC_HAS_BUILDARGS = 0x0004,
e128626c 24 MOUSEf_XC_CONSTRUCTOR_IS_STRICT
25 = 0x0008,
aa2d2e2c 26
27 MOUSEf_XC_mask = 0xFFFF /* not used */
28};
29
a39e9541 30/* Mouse XS Metaclass object */
31enum mouse_xc_ix_t{
4e7e3250 32 MOUSE_XC_FLAGS,
33
a39e9541 34 MOUSE_XC_GEN, /* class generation */
aa2d2e2c 35 MOUSE_XC_STASH, /* symbol table hash */
aa2d2e2c 36
a39e9541 37 MOUSE_XC_ATTRALL, /* all the attributes */
38 MOUSE_XC_BUILDALL, /* all the BUILD methods */
39 MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */
40
41 MOUSE_XC_last
42};
43
cb60d0b5 44enum mouse_modifier_t {
45 MOUSE_M_BEFORE,
46 MOUSE_M_AROUND,
47 MOUSE_M_AFTER,
48};
49
a39e9541 50static MGVTBL mouse_xc_vtbl; /* for identity */
51
05e658dc 52HV*
53mouse_get_namespace(pTHX_ SV* const meta) {
54 SV* const package = get_slot(meta, mouse_package);
55 if(!(package && SvOK(package))){
56 croak("No package name defined for metaclass");
57 }
58 return gv_stashsv(package, GV_ADDMULTI);
59}
60
abbcd124 61static AV*
62mouse_calculate_all_attributes(pTHX_ SV* const metaclass) {
63 SV* const avref = mcall0s(metaclass, "_calculate_all_attributes");
64 if(!(SvROK(avref) && SvTYPE(SvRV(avref)) == SVt_PVAV)) {
65 croak("$meta->_calculate_all_attributes did not return an ARRAY reference");
a39e9541 66 }
abbcd124 67 return (AV*)SvRV(avref);
a39e9541 68}
743ca82e 69
fe5044b1 70XS(XS_Mouse__Object_BUILDARGS); /* prototype */
71
aa2d2e2c 72static int
73mouse_class_has_custom_buildargs(pTHX_ HV* const stash) {
aa2d2e2c 74 GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0);
aa2d2e2c 75
12872cc1 76 return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS;
aa2d2e2c 77}
78
bdf225bb 79static AV*
80mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, AV* const xc) {
81 HV* const stash = MOUSE_xc_stash(xc);
a39e9541 82 AV* const linearized_isa = mro_get_linear_isa(stash);
abc8d595 83 I32 const len = AvFILLp(linearized_isa) + 1;
a39e9541 84 I32 i;
aa2d2e2c 85 U32 flags = 0x00;
a39e9541 86 AV* const buildall = newAV();
87 AV* const demolishall = newAV();
abbcd124 88 AV* attrall;
da4432f3 89
a39e9541 90 ENTER;
91 SAVETMPS;
da4432f3 92
a39e9541 93 /* old data will be delete at the end of the perl scope */
94 av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00);
95 av_delete(xc, MOUSE_XC_BUILDALL, 0x00);
96 av_delete(xc, MOUSE_XC_ATTRALL, 0x00);
743ca82e 97
a39e9541 98 SvREFCNT_inc_simple_void_NN(linearized_isa);
99 sv_2mortal((SV*)linearized_isa);
da4432f3 100
a39e9541 101 /* update */
da4432f3 102
abbcd124 103 av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall);
104 av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
105
106 attrall = mouse_calculate_all_attributes(aTHX_ metaclass);
107 SvREFCNT_inc_simple_void_NN(attrall);
108 av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall);
109
4e7e3250 110 if(predicate_calls(metaclass, "is_immutable")){
aa2d2e2c 111 flags |= MOUSEf_XC_IS_IMMUTABLE;
112 }
113
4e7e3250 114 if(predicate_calls(metaclass, "is_anon_class")){
115 flags |= MOUSEf_XC_IS_ANON;
116 }
117
aa2d2e2c 118 if(mouse_class_has_custom_buildargs(aTHX_ stash)){
119 flags |= MOUSEf_XC_HAS_BUILDARGS;
120 }
121
fb4ddd88 122 if(predicate_calls(metaclass, "strict_constructor")){
e128626c 123 flags |= MOUSEf_XC_CONSTRUCTOR_IS_STRICT;
124 }
125
4e7e3250 126 av_store(xc, MOUSE_XC_FLAGS, newSVuv(flags));
da4432f3 127
a39e9541 128 for(i = 0; i < len; i++){
047d7af0 129 SV* const klass = MOUSE_av_at(linearized_isa, i);
074a414d 130 HV* const st = gv_stashsv(klass, TRUE);
a39e9541 131 GV* gv;
da4432f3 132
074a414d 133 gv = stash_fetchs(st, "BUILD", FALSE);
a39e9541 134 if(gv && GvCVu(gv)){
074a414d 135 av_unshift(buildall, 1);
136 av_store(buildall, 0, newRV_inc((SV*)GvCV(gv)));
a39e9541 137 }
da4432f3 138
074a414d 139 gv = stash_fetchs(st, "DEMOLISH", FALSE);
a39e9541 140 if(gv && GvCVu(gv)){
141 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
142 }
a39e9541 143 }
da4432f3 144
a39e9541 145 FREETMPS;
146 LEAVE;
da4432f3 147
a39e9541 148 sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash));
bdf225bb 149 return xc;
a39e9541 150}
da4432f3 151
3001d9ac 152static AV*
bdf225bb 153mouse_get_xc_wo_check(pTHX_ SV* const metaclass) {
a39e9541 154 AV* xc;
a39e9541 155 MAGIC* mg;
156
157 if(!IsObject(metaclass)){
158 croak("Not a Mouse metaclass");
159 }
da4432f3 160
a39e9541 161 mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00);
162 if(!mg){
05e658dc 163 /* cache stash for performance */
bdf225bb 164 HV* const stash = mouse_get_namespace(aTHX_ metaclass);
a39e9541 165 xc = newAV();
da4432f3 166
05e658dc 167 mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext,
168 &mouse_xc_vtbl, NULL, 0);
a39e9541 169 SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */
da4432f3 170
a39e9541 171 av_extend(xc, MOUSE_XC_last - 1);
aa2d2e2c 172
a5c683f6 173 av_store(xc, MOUSE_XC_GEN, newSVuv(0U));
aa2d2e2c 174 av_store(xc, MOUSE_XC_STASH, (SV*)stash);
175 SvREFCNT_inc_simple_void_NN(stash);
da4432f3 176 }
a39e9541 177 else{
a39e9541 178 xc = (AV*)MOUSE_mg_obj(mg);
da4432f3 179
a39e9541 180 assert(xc);
181 assert(SvTYPE(xc) == SVt_PVAV);
182 }
bdf225bb 183 return xc;
184}
da4432f3 185
bdf225bb 186static int
48a4a7b4 187mouse_xc_is_fresh(pTHX_ AV* const xc) {
bdf225bb 188 HV* const stash = MOUSE_xc_stash(xc);
189 SV* const gen = MOUSE_xc_gen(xc);
190 if(SvUVX(gen) != 0U && MOUSE_xc_flags(xc) & MOUSEf_XC_IS_IMMUTABLE) {
191 return TRUE;
a5c683f6 192 }
bdf225bb 193 return SvUVX(gen) == mro_get_pkg_gen(stash);
194}
a5c683f6 195
66aa0bfc 196STATIC_INLINE AV*
bdf225bb 197mouse_get_xc(pTHX_ SV* const metaclass) {
198 AV* const xc = mouse_get_xc_wo_check(aTHX_ metaclass);
199 return mouse_xc_is_fresh(aTHX_ xc)
200 ? xc
201 : mouse_class_update_xc(aTHX_ metaclass, xc);
202}
a39e9541 203
bdf225bb 204static AV*
205mouse_get_xc_if_fresh(pTHX_ SV* const metaclass) {
206 AV* const xc = mouse_get_xc_wo_check(aTHX_ metaclass);
207 return mouse_xc_is_fresh(aTHX_ xc)
208 ? xc
209 : NULL;
a39e9541 210}
211
3001d9ac 212static HV*
074a414d 213mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) {
aa2d2e2c 214 HV* args;
074a414d 215
216 /* shift @_ */
217 ax++;
218 items--;
219
220 if(items == 1){
221 SV* const args_ref = ST(0);
aa2d2e2c 222 if(!IsHashRef(args_ref)){
223 if(!metaclass){ metaclass = get_metaclass(klass); }
224 mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref");
225 }
226 args = newHVhv((HV*)SvRV(args_ref));
227 sv_2mortal((SV*)args);
228 }
229 else{
230 I32 i;
231
074a414d 232 if( (items % 2) != 0 ){
aa2d2e2c 233 if(!metaclass){ metaclass = get_metaclass(klass); }
234 mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()");
235 }
236
bc97157e 237 args = newHV_mortal();
074a414d 238 for(i = 0; i < items; i += 2){
aa2d2e2c 239 (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U);
240 }
241
242 }
243 return args;
244}
245
3001d9ac 246static void
e128626c 247mouse_report_unknown_args(pTHX_ SV* const meta, AV* const attrs, HV* const args) {
248 HV* const attr_map = newHV_mortal();
249 SV* const unknown = newSVpvs_flags("", SVs_TEMP);
250 I32 const len = AvFILLp(attrs) + 1;
251 I32 i;
252 HE* he;
253
254 for(i = 0; i < len; i++){
255 SV* const attr = MOUSE_av_at(attrs, i);
256 AV* const xa = mouse_get_xa(aTHX_ attr);
257 SV* const init_arg = MOUSE_xa_init_arg(xa);
258 if(SvOK(init_arg)){
259 (void)hv_store_ent(attr_map, init_arg, &PL_sv_undef, 0U);
260 }
261 }
262
263 hv_iterinit(args);
264 while((he = hv_iternext(args))){
265 SV* const key = hv_iterkeysv(he);
266 if(!hv_exists_ent(attr_map, key, 0U)){
267 sv_catpvf(unknown, "%"SVf", ", key);
268 }
269 }
270
271 if(SvCUR(unknown) > 0){
272 SvCUR(unknown) -= 2; /* chop "," */
273 }
274 else{
275 sv_setpvs(unknown, "(unknown)");
276 }
277
278 mouse_throw_error(meta, NULL,
279 "Unknown attribute passed to the constructor of %"SVf": %"SVf,
280 mcall0(meta, mouse_name), unknown);
281}
282
283
284
285static void
cb6a9721 286mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const is_cloning) {
d4712779 287 AV* const xc = mouse_get_xc(aTHX_ meta);
288 AV* const attrs = MOUSE_xc_attrall(xc);
60cd9f19 289 I32 const len = AvFILLp(attrs) + 1;
d4712779 290 I32 i;
4e7e3250 291 AV* triggers_queue = NULL;
05089b62 292 I32 used = 0;
4e7e3250 293
074a414d 294 assert(meta || object);
295 assert(args);
296 assert(SvTYPE(args) == SVt_PVHV);
297
6463e839 298 if(mg_find((SV*)args, PERL_MAGIC_tied)){
299 croak("You cannot use tied HASH reference as initializing arguments");
300 }
301
e128626c 302 /* for each attribute */
d4712779 303 for(i = 0; i < len; i++){
6bc5b495 304 SV* const attr = MOUSE_av_at(attrs, i);
305 AV* const xa = mouse_get_xa(aTHX_ attr);
4e7e3250 306
307 SV* const slot = MOUSE_xa_slot(xa);
308 U16 const flags = (U16)MOUSE_xa_flags(xa);
309 SV* const init_arg = MOUSE_xa_init_arg(xa);
310 HE* he;
311
312 if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){
313 SV* value = HeVAL(he);
314 if(flags & MOUSEf_ATTR_HAS_TC){
315 value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
316 }
ca8e67d6 317 value = set_slot(object, slot, value);
f1575d9f 318 if(flags & MOUSEf_ATTR_IS_WEAK_REF && SvROK(value)){
4e7e3250 319 weaken_slot(object, slot);
320 }
cb6a9721 321 if(flags & MOUSEf_ATTR_HAS_TRIGGER){
4e7e3250 322 AV* const pair = newAV();
323 av_push(pair, newSVsv( mcall0s(attr, "trigger") ));
324 av_push(pair, newSVsv(value));
325
cb6a9721 326 if(!triggers_queue) {
327 triggers_queue = newAV_mortal();
328 }
4e7e3250 329 av_push(triggers_queue, (SV*)pair);
330 }
e128626c 331 used++;
4e7e3250 332 }
6c7491f2 333 else { /* no init arg */
4e7e3250 334 if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){
6c7491f2 335 /* skip if the object has the slot (it occurs on cloning/reblessing) */
336 if(!(flags & MOUSEf_ATTR_IS_LAZY) && !has_slot(object, slot)){
4e7e3250 337 mouse_xa_set_default(aTHX_ xa, object);
338 }
339 }
f1575d9f 340 else if(is_cloning) {
341 if(flags & MOUSEf_ATTR_IS_WEAK_REF){
342 SV* const value = get_slot(object, slot);
343 if(SvROK(value)) {
344 weaken_slot(object, slot);
345 }
346 }
347 }
348 /* don't check "required" while cloning (or reblesseing) */
349 else if(flags & MOUSEf_ATTR_IS_REQUIRED) {
4e7e3250 350 mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot);
351 }
352 }
e128626c 353 } /* for each attribute */
354
05089b62 355 if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT
356 && used < (I32)HvUSEDKEYS(args)){
e128626c 357 mouse_report_unknown_args(aTHX_ meta, attrs, args);
358 }
4e7e3250 359
360 if(triggers_queue){
60cd9f19 361 I32 const len = AvFILLp(triggers_queue) + 1;
4e7e3250 362 for(i = 0; i < len; i++){
363 AV* const pair = (AV*)AvARRAY(triggers_queue)[i];
364 SV* const trigger = AvARRAY(pair)[0];
365 SV* const value = AvARRAY(pair)[1];
366
367 mcall1(object, trigger, value);
368 }
369 }
370
371 if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
ca8e67d6 372 (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta);
d4712779 373 }
aa2d2e2c 374}
375
f6c81f00 376STATIC_INLINE SV*
1bbf8369 377mouse_initialize_metaclass(pTHX_ SV* const klass) {
f6c81f00 378 SV* const meta = get_metaclass(klass);
379 if(LIKELY(SvOK(meta))){
380 return meta;
1bbf8369 381 }
f6c81f00 382 return mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP),
383 "initialize", klass);
1bbf8369 384}
385
adb5eb76 386static void
387mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) {
388 AV* const buildall = MOUSE_xc_buildall(xc);
389 I32 const len = AvFILLp(buildall) + 1;
390 I32 i;
391 for(i = 0; i < len; i++){
392 dSP;
393
394 PUSHMARK(SP);
395 EXTEND(SP, 2);
396 PUSHs(object);
397 PUSHs(args);
398 PUTBACK;
399
b3cd4c14 400 call_sv_safe(AvARRAY(buildall)[i], G_VOID);
accc9fc2 401
402 /* discard a scalar which G_VOID returns */
403 SPAGAIN;
404 (void)POPs;
405 PUTBACK;
adb5eb76 406 }
407}
408
cb60d0b5 409static AV*
410mouse_get_modifier_storage(pTHX_
411 SV* const meta,
412 enum mouse_modifier_t const m, SV* const name) {
413 static const char* const keys[] = {
414 "before",
415 "around",
416 "after",
417 };
418 SV* const key = sv_2mortal(Perl_newSVpvf(aTHX_ "%s_method_modifiers", keys[m]));
419 SV* table;
420 SV* storage_ref;
421
d06d9266 422 must_defined(name, "a method name");
cb60d0b5 423
424 table = get_slot(meta, key);
425
426 if(!table){
427 /* $meta->{$key} = {} */
428 table = sv_2mortal(newRV_noinc((SV*)newHV()));
429 set_slot(meta, key, table);
430 }
431
432 storage_ref = get_slot(table, name);
433
434 if(!storage_ref){
435 storage_ref = sv_2mortal(newRV_noinc((SV*)newAV()));
436 set_slot(table, name, storage_ref);
437 }
438 else{
439 if(!IsArrayRef(storage_ref)){
440 croak("Modifier strorage for '%s' is not an ARRAY reference", keys[m]);
441 }
442 }
443
444 return (AV*)SvRV(storage_ref);
445}
446
91ee66cb 447static
448XSPROTO(XS_Mouse_value_holder) {
902174eb 449 dVAR; dXSARGS;
450 SV* const value = (SV*)XSANY.any_ptr;
451 assert(value);
452 PERL_UNUSED_VAR(items);
453 ST(0) = value;
454 XSRETURN(1);
455}
456
fe5044b1 457DECL_BOOT(Mouse__Util);
458DECL_BOOT(Mouse__Util__TypeConstraints);
459DECL_BOOT(Mouse__Meta__Method__Accessor__XS);
460DECL_BOOT(Mouse__Meta__Attribute);
461
646c0371 462MODULE = Mouse PACKAGE = Mouse
df6dd016 463
464PROTOTYPES: DISABLE
465
cccb83de 466BOOT:
902174eb 467{
cccb83de 468 mouse_package = newSVpvs_share("package");
469 mouse_namespace = newSVpvs_share("namespace");
3e44140b 470 mouse_methods = newSVpvs_share("methods");
a5df48e5 471 mouse_name = newSVpvs_share("name");
d39ac61c 472 mouse_coerce = newSVpvs_share("coerce");
3e44140b 473
047d7af0 474 mouse_get_attribute = newSVpvs_share("get_attribute");
475 mouse_get_attribute_list = newSVpvs_share("get_attribute_list");
da4432f3 476
fe5044b1 477 CALL_BOOT(Mouse__Util);
478 CALL_BOOT(Mouse__Util__TypeConstraints);
479 CALL_BOOT(Mouse__Meta__Method__Accessor__XS);
480 CALL_BOOT(Mouse__Meta__Attribute);
902174eb 481}
cccb83de 482
7d96ae4d 483MODULE = Mouse PACKAGE = Mouse::Meta::Module
484
485BOOT:
486 INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package);
487 INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods);
488 INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes);
489
490HV*
491namespace(SV* self)
492CODE:
493{
05e658dc 494 RETVAL = mouse_get_namespace(aTHX_ self);
7d96ae4d 495}
496OUTPUT:
497 RETVAL
498
3e44140b 499# ignore extra arguments for extensibility
500void
501add_method(SV* self, SV* name, SV* code, ...)
502CODE:
503{
6a97bbda 504 SV* const package = get_slot(self, mouse_package); /* $self->{package} */
505 SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */
3e44140b 506 GV* gv;
507 SV* code_ref;
508
509 if(!(package && SvOK(package))){
510 croak("No package name defined");
511 }
512
d06d9266 513 must_defined(name, "a method name");
514 must_ref (code, "a CODE reference", SVt_NULL); /* any reftype is OK */
3e44140b 515
516 code_ref = code;
517 if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){
518 SV* sv = code_ref; /* used in tryAMAGICunDEREF */
519 SV** sp = &sv; /* used in tryAMAGICunDEREF */
520 tryAMAGICunDEREF(to_cv); /* try \&{$code} */
d06d9266 521 must_ref(code, "a CODE reference", SVt_PVCV);
3e44140b 522 code_ref = sv;
523 }
524
525 /* *{$package . '::' . $name} -> *gv */
526 gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV);
d67f600d 527 mouse_install_sub(aTHX_ gv, code_ref);
9acbbf0a 528 /* CvMETHOD_on((CV*)SvRV(code_ref)); */
ca8e67d6 529 (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */
3e44140b 530}
531
43165725 532MODULE = Mouse PACKAGE = Mouse::Meta::Class
533
534BOOT:
902174eb 535{
536 CV* xsub;
537
43165725 538 INSTALL_SIMPLE_READER(Class, roles);
539 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
80efe911 540 INSTALL_SIMPLE_READER(Class, is_immutable);
3a2ccd77 541
fad99075 542 INSTALL_INHERITABLE_CLASS_ACCESSOR(strict_constructor);
543
787f84c2 544 INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method");
545 INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute");
546 INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS");
547 INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS");
a5c683f6 548
902174eb 549 xsub = newXS("Mouse::Meta::Method::Constructor::XS::_generate_constructor",
550 XS_Mouse_value_holder, file);
551 CvXSUBANY(xsub).any_ptr
552 = newRV_inc((SV*)get_cvs("Mouse::Object::new", GV_ADD));
553
554 xsub = newXS("Mouse::Meta::Method::Destructor::XS::_generate_destructor",
555 XS_Mouse_value_holder, file);
556 CvXSUBANY(xsub).any_ptr
557 = newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", GV_ADD));
558}
559
a5c683f6 560
cccb83de 561void
562linearized_isa(SV* self)
563PPCODE:
564{
05e658dc 565 /* MOUSE_xc_stash() is not available because the xc system depends on
566 linearized_isa() */
567 HV* const stash = mouse_get_namespace(aTHX_ self);
568 AV* const linearized_isa = mro_get_linear_isa(stash);
e0f85c26 569 I32 const len = AvFILLp(linearized_isa) + 1;
cccb83de 570 I32 i;
cccb83de 571 EXTEND(SP, len);
572 for(i = 0; i < len; i++){
573 PUSHs(AvARRAY(linearized_isa)[i]);
574 }
575}
576
da4432f3 577void
578get_all_attributes(SV* self)
579PPCODE:
580{
0aad0266 581 AV* const xc = mouse_get_xc(aTHX_ self);
ae34e077 582 AV* const all_attrs = MOUSE_xc_attrall(xc);
da4432f3 583 I32 const len = AvFILLp(all_attrs) + 1;
584 I32 i;
585
586 EXTEND(SP, len);
587 for(i = 0; i < len; i++){
588 PUSHs( MOUSE_av_at(all_attrs, i) );
589 }
590}
441964ce 591
fcdc12ac 592void
ba153b33 593new_object(SV* meta, ...)
aa2d2e2c 594CODE:
595{
aa2d2e2c 596 AV* const xc = mouse_get_xc(aTHX_ meta);
074a414d 597 HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items);
fcdc12ac 598 SV* object;
d4712779 599
fcdc12ac 600 object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
601 mouse_class_initialize_object(aTHX_ meta, object, args, FALSE);
e0f85c26 602 mouse_buildall(aTHX_ xc, object, sv_2mortal(newRV_inc((SV*)args)));
fcdc12ac 603 ST(0) = object; /* because object is mortal, we should return it as is */
604 XSRETURN(1);
aa2d2e2c 605}
aa2d2e2c 606
fcdc12ac 607void
13b99908 608clone_object(SV* meta, SV* object, ...)
609CODE:
610{
611 AV* const xc = mouse_get_xc(aTHX_ meta);
612 HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax + 1, items - 1);
fcdc12ac 613 SV* proto;
13b99908 614
615 if(!mouse_is_an_instance_of(aTHX_ MOUSE_xc_stash(xc), object)) {
616 mouse_throw_error(meta, object,
617 "You must pass an instance of the metaclass (%"SVf"), not (%"SVf")",
618 mcall0(meta, mouse_name), object);
619 }
620
fcdc12ac 621 proto = mouse_instance_clone(aTHX_ object);
622 mouse_class_initialize_object(aTHX_ meta, proto, args, TRUE);
623 ST(0) = proto; /* because object is mortal, we should return it as is */
624 XSRETURN(1);
13b99908 625}
13b99908 626
aa2d2e2c 627void
cb6a9721 628_initialize_object(SV* meta, SV* object, HV* args, bool is_cloning = FALSE)
aa2d2e2c 629CODE:
630{
cb6a9721 631 mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning);
aa2d2e2c 632}
633
6f09819f 634void
635_invalidate_metaclass_cache(SV* meta)
636CODE:
637{
638 AV* const xc = mouse_get_xc_if_fresh(aTHX_ meta);
639 if(xc) {
640 SV* const gen = MOUSE_xc_gen(xc);
641 sv_setuv(gen, 0U);
642 }
643 delete_slot(meta, newSVpvs_flags("_mouse_cache_", SVs_TEMP));
644}
645
646
43165725 647MODULE = Mouse PACKAGE = Mouse::Meta::Role
648
649BOOT:
650 INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles);
651 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id);
652
787f84c2 653 INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method");
e058b279 654
cb60d0b5 655void
656add_before_modifier(SV* self, SV* name, SV* modifier)
657CODE:
658{
fe5044b1 659 av_push(mouse_get_modifier_storage(aTHX_ self, (enum mouse_modifier_t)ix, name), newSVsv(modifier));
cb60d0b5 660}
661ALIAS:
662 add_before_method_modifier = MOUSE_M_BEFORE
663 add_around_method_modifier = MOUSE_M_AROUND
664 add_after_method_modifier = MOUSE_M_AFTER
665
666void
667get_before_modifiers(SV* self, SV* name)
668ALIAS:
669 get_before_method_modifiers = MOUSE_M_BEFORE
670 get_around_method_modifiers = MOUSE_M_AROUND
671 get_after_method_modifiers = MOUSE_M_AFTER
672PPCODE:
673{
e0f85c26 674 AV* const storage = mouse_get_modifier_storage(aTHX_ self,
675 (enum mouse_modifier_t)ix, name);
cb60d0b5 676 I32 const len = av_len(storage) + 1;
677 if(GIMME_V == G_ARRAY) {
678 I32 i;
679 EXTEND(SP, len);
680 for(i = 0; i < len; i++){
681 PUSHs(*av_fetch(storage, i, TRUE));
682 }
683 }
684 else{
685 mPUSHi(len);
686 }
687}
688
b4dc9315 689void
690add_metaclass_accessor(SV* self, SV* name)
691CODE:
692{
4bc0a681 693 SV* const klass = mcall0(self, mouse_name);
b4dc9315 694 const char* fq_name = form("%"SVf"::%"SVf, klass, name);
695 STRLEN keylen;
696 const char* const key = SvPV_const(name, keylen);
697 mouse_simple_accessor_generate(aTHX_ fq_name, key, keylen,
698 XS_Mouse_inheritable_class_accessor, NULL, 0);
699}
700
aa2d2e2c 701MODULE = Mouse PACKAGE = Mouse::Object
43165725 702
fcdc12ac 703void
074a414d 704new(SV* klass, ...)
705CODE:
706{
1bbf8369 707 SV* const meta = mouse_initialize_metaclass(aTHX_ klass);
074a414d 708 AV* const xc = mouse_get_xc(aTHX_ meta);
709 UV const flags = MOUSE_xc_flags(xc);
710 SV* args;
fcdc12ac 711 SV* object;
074a414d 712
713 /* BUILDARGS */
714 if(flags & MOUSEf_XC_HAS_BUILDARGS){
adb5eb76 715 I32 i;
074a414d 716 SPAGAIN;
717
718 PUSHMARK(SP);
719 EXTEND(SP, items);
720 for(i = 0; i < items; i++){
721 PUSHs(ST(i));
722 }
b3cd4c14 723
074a414d 724 PUTBACK;
b3cd4c14 725 call_method_safes("BUILDARGS", G_SCALAR);
726
074a414d 727 SPAGAIN;
728 args = POPs;
729 PUTBACK;
730
731 if(!IsHashRef(args)){
732 croak("BUILDARGS did not return a HASH reference");
733 }
734 }
735 else{
736 args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items));
737 sv_2mortal(args);
738 }
739
740 /* new_object */
fcdc12ac 741 object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
742 mouse_class_initialize_object(aTHX_ meta, object, (HV*)SvRV(args), FALSE);
e0f85c26 743 /* BUILDALL */
744 mouse_buildall(aTHX_ xc, object, args);
fcdc12ac 745 ST(0) = object; /* because object is mortal, we should return it as is */
746 XSRETURN(1);
074a414d 747}
074a414d 748
a5c683f6 749void
750DESTROY(SV* object)
adb5eb76 751ALIAS:
752 DESTROY = 0
753 DEMOLISHALL = 1
aa2d2e2c 754CODE:
755{
a5c683f6 756 SV* const meta = get_metaclass(object);
bdf225bb 757 AV* xc;
a5c683f6 758 AV* demolishall;
bdf225bb 759 I32 len;
760 I32 i;
a5c683f6 761
762 if(!IsObject(object)){
31113a4a 763 croak("You must not call %s as a class method",
764 ix == 0 ? "DESTROY" : "DEMOLISHALL");
a5c683f6 765 }
766
bdf225bb 767 if(SvOK(meta) && (xc = mouse_get_xc_if_fresh(aTHX_ meta))) {
a5c683f6 768 demolishall = MOUSE_xc_demolishall(xc);
769 }
adb5eb76 770 else { /* The metaclass is already destroyed */
a5c683f6 771 AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object)));
7efbc77d 772
a5c683f6 773 len = AvFILLp(linearized_isa) + 1;
7efbc77d 774
a5c683f6 775 demolishall = newAV_mortal();
776 for(i = 0; i < len; i++){
777 SV* const klass = MOUSE_av_at(linearized_isa, i);
778 HV* const st = gv_stashsv(klass, TRUE);
779 GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE);
780 if(gv && GvCVu(gv)){
781 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
782 }
783 }
784 }
785
bdf225bb 786 len = AvFILLp(demolishall) + 1;
a5c683f6 787 if(len > 0){
5bc4e0be 788 SV* const in_global_destruction = boolSV(PL_dirty);
789 SAVEI32(PL_statusvalue); /* local $? */
9bd5683d 790 PL_statusvalue = 0;
70425827 791
a5c683f6 792 SAVESPTR(ERRSV); /* local $@ */
b3cd4c14 793 ERRSV = sv_newmortal();
794
795 EXTEND(SP, 2);
a5c683f6 796
a5c683f6 797 for(i = 0; i < len; i++){
f8beb873 798 SPAGAIN;
a5c683f6 799
800 PUSHMARK(SP);
b3cd4c14 801 PUSHs(object);
5bc4e0be 802 PUSHs(in_global_destruction);
a5c683f6 803 PUTBACK;
804
accc9fc2 805 call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL);
806
807 /* discard a scalar which G_VOID returns */
808 SPAGAIN;
809 (void)POPs;
810 PUTBACK;
811
6ad77996 812 if(sv_true(ERRSV)){
a5c683f6 813 SV* const e = newSVsv(ERRSV);
814
815 FREETMPS;
816 LEAVE;
817
818 sv_setsv(ERRSV, e);
819 SvREFCNT_dec(e);
820 croak(NULL); /* rethrow */
821 }
822 }
823 }
824}
825
826HV*
827BUILDARGS(SV* klass, ...)
7efbc77d 828CODE:
829{
a5c683f6 830 RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items);
7efbc77d 831}
832OUTPUT:
833 RETVAL
834
a5c683f6 835
adb5eb76 836void
837BUILDALL(SV* self, SV* args)
838CODE:
839{
f50e50d6 840 SV* const meta = get_metaclass(self);
841 AV* const xc = mouse_get_xc(aTHX_ meta);
adb5eb76 842
d06d9266 843 must_ref(args, "a HASH reference to BUILDALL", SVt_PVHV);
adb5eb76 844 mouse_buildall(aTHX_ xc, self, args);
845}