Resolve RT #67412: C++ stule comments cause prolems in AIX
[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);
4e7e3250 318 if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){
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 }
6c7491f2 340 /* don't check while cloning (or reblesseing) */
341 else if(!is_cloning && flags & MOUSEf_ATTR_IS_REQUIRED) {
4e7e3250 342 mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot);
343 }
344 }
e128626c 345 } /* for each attribute */
346
05089b62 347 if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT
348 && used < (I32)HvUSEDKEYS(args)){
e128626c 349 mouse_report_unknown_args(aTHX_ meta, attrs, args);
350 }
4e7e3250 351
352 if(triggers_queue){
60cd9f19 353 I32 const len = AvFILLp(triggers_queue) + 1;
4e7e3250 354 for(i = 0; i < len; i++){
355 AV* const pair = (AV*)AvARRAY(triggers_queue)[i];
356 SV* const trigger = AvARRAY(pair)[0];
357 SV* const value = AvARRAY(pair)[1];
358
359 mcall1(object, trigger, value);
360 }
361 }
362
363 if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
ca8e67d6 364 (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta);
d4712779 365 }
aa2d2e2c 366}
367
f6c81f00 368STATIC_INLINE SV*
1bbf8369 369mouse_initialize_metaclass(pTHX_ SV* const klass) {
f6c81f00 370 SV* const meta = get_metaclass(klass);
371 if(LIKELY(SvOK(meta))){
372 return meta;
1bbf8369 373 }
f6c81f00 374 return mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP),
375 "initialize", klass);
1bbf8369 376}
377
adb5eb76 378static void
379mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) {
380 AV* const buildall = MOUSE_xc_buildall(xc);
381 I32 const len = AvFILLp(buildall) + 1;
382 I32 i;
383 for(i = 0; i < len; i++){
384 dSP;
385
386 PUSHMARK(SP);
387 EXTEND(SP, 2);
388 PUSHs(object);
389 PUSHs(args);
390 PUTBACK;
391
b3cd4c14 392 call_sv_safe(AvARRAY(buildall)[i], G_VOID);
accc9fc2 393
394 /* discard a scalar which G_VOID returns */
395 SPAGAIN;
396 (void)POPs;
397 PUTBACK;
adb5eb76 398 }
399}
400
cb60d0b5 401static AV*
402mouse_get_modifier_storage(pTHX_
403 SV* const meta,
404 enum mouse_modifier_t const m, SV* const name) {
405 static const char* const keys[] = {
406 "before",
407 "around",
408 "after",
409 };
410 SV* const key = sv_2mortal(Perl_newSVpvf(aTHX_ "%s_method_modifiers", keys[m]));
411 SV* table;
412 SV* storage_ref;
413
d06d9266 414 must_defined(name, "a method name");
cb60d0b5 415
416 table = get_slot(meta, key);
417
418 if(!table){
419 /* $meta->{$key} = {} */
420 table = sv_2mortal(newRV_noinc((SV*)newHV()));
421 set_slot(meta, key, table);
422 }
423
424 storage_ref = get_slot(table, name);
425
426 if(!storage_ref){
427 storage_ref = sv_2mortal(newRV_noinc((SV*)newAV()));
428 set_slot(table, name, storage_ref);
429 }
430 else{
431 if(!IsArrayRef(storage_ref)){
432 croak("Modifier strorage for '%s' is not an ARRAY reference", keys[m]);
433 }
434 }
435
436 return (AV*)SvRV(storage_ref);
437}
438
91ee66cb 439static
440XSPROTO(XS_Mouse_value_holder) {
902174eb 441 dVAR; dXSARGS;
442 SV* const value = (SV*)XSANY.any_ptr;
443 assert(value);
444 PERL_UNUSED_VAR(items);
445 ST(0) = value;
446 XSRETURN(1);
447}
448
fe5044b1 449DECL_BOOT(Mouse__Util);
450DECL_BOOT(Mouse__Util__TypeConstraints);
451DECL_BOOT(Mouse__Meta__Method__Accessor__XS);
452DECL_BOOT(Mouse__Meta__Attribute);
453
646c0371 454MODULE = Mouse PACKAGE = Mouse
df6dd016 455
456PROTOTYPES: DISABLE
457
cccb83de 458BOOT:
902174eb 459{
cccb83de 460 mouse_package = newSVpvs_share("package");
461 mouse_namespace = newSVpvs_share("namespace");
3e44140b 462 mouse_methods = newSVpvs_share("methods");
a5df48e5 463 mouse_name = newSVpvs_share("name");
d39ac61c 464 mouse_coerce = newSVpvs_share("coerce");
3e44140b 465
047d7af0 466 mouse_get_attribute = newSVpvs_share("get_attribute");
467 mouse_get_attribute_list = newSVpvs_share("get_attribute_list");
da4432f3 468
fe5044b1 469 CALL_BOOT(Mouse__Util);
470 CALL_BOOT(Mouse__Util__TypeConstraints);
471 CALL_BOOT(Mouse__Meta__Method__Accessor__XS);
472 CALL_BOOT(Mouse__Meta__Attribute);
902174eb 473}
cccb83de 474
7d96ae4d 475MODULE = Mouse PACKAGE = Mouse::Meta::Module
476
477BOOT:
478 INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package);
479 INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods);
480 INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes);
481
482HV*
483namespace(SV* self)
484CODE:
485{
05e658dc 486 RETVAL = mouse_get_namespace(aTHX_ self);
7d96ae4d 487}
488OUTPUT:
489 RETVAL
490
3e44140b 491# ignore extra arguments for extensibility
492void
493add_method(SV* self, SV* name, SV* code, ...)
494CODE:
495{
6a97bbda 496 SV* const package = get_slot(self, mouse_package); /* $self->{package} */
497 SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */
3e44140b 498 GV* gv;
499 SV* code_ref;
500
501 if(!(package && SvOK(package))){
502 croak("No package name defined");
503 }
504
d06d9266 505 must_defined(name, "a method name");
506 must_ref (code, "a CODE reference", SVt_NULL); /* any reftype is OK */
3e44140b 507
508 code_ref = code;
509 if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){
510 SV* sv = code_ref; /* used in tryAMAGICunDEREF */
511 SV** sp = &sv; /* used in tryAMAGICunDEREF */
512 tryAMAGICunDEREF(to_cv); /* try \&{$code} */
d06d9266 513 must_ref(code, "a CODE reference", SVt_PVCV);
3e44140b 514 code_ref = sv;
515 }
516
517 /* *{$package . '::' . $name} -> *gv */
518 gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV);
d67f600d 519 mouse_install_sub(aTHX_ gv, code_ref);
9acbbf0a 520 /* CvMETHOD_on((CV*)SvRV(code_ref)); */
ca8e67d6 521 (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */
3e44140b 522}
523
43165725 524MODULE = Mouse PACKAGE = Mouse::Meta::Class
525
526BOOT:
902174eb 527{
528 CV* xsub;
529
43165725 530 INSTALL_SIMPLE_READER(Class, roles);
531 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
80efe911 532 INSTALL_SIMPLE_READER(Class, is_immutable);
3a2ccd77 533
fad99075 534 INSTALL_INHERITABLE_CLASS_ACCESSOR(strict_constructor);
535
787f84c2 536 INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method");
537 INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute");
538 INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS");
539 INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS");
a5c683f6 540
902174eb 541 xsub = newXS("Mouse::Meta::Method::Constructor::XS::_generate_constructor",
542 XS_Mouse_value_holder, file);
543 CvXSUBANY(xsub).any_ptr
544 = newRV_inc((SV*)get_cvs("Mouse::Object::new", GV_ADD));
545
546 xsub = newXS("Mouse::Meta::Method::Destructor::XS::_generate_destructor",
547 XS_Mouse_value_holder, file);
548 CvXSUBANY(xsub).any_ptr
549 = newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", GV_ADD));
550}
551
a5c683f6 552
cccb83de 553void
554linearized_isa(SV* self)
555PPCODE:
556{
05e658dc 557 /* MOUSE_xc_stash() is not available because the xc system depends on
558 linearized_isa() */
559 HV* const stash = mouse_get_namespace(aTHX_ self);
560 AV* const linearized_isa = mro_get_linear_isa(stash);
e0f85c26 561 I32 const len = AvFILLp(linearized_isa) + 1;
cccb83de 562 I32 i;
cccb83de 563 EXTEND(SP, len);
564 for(i = 0; i < len; i++){
565 PUSHs(AvARRAY(linearized_isa)[i]);
566 }
567}
568
da4432f3 569void
570get_all_attributes(SV* self)
571PPCODE:
572{
0aad0266 573 AV* const xc = mouse_get_xc(aTHX_ self);
ae34e077 574 AV* const all_attrs = MOUSE_xc_attrall(xc);
da4432f3 575 I32 const len = AvFILLp(all_attrs) + 1;
576 I32 i;
577
578 EXTEND(SP, len);
579 for(i = 0; i < len; i++){
580 PUSHs( MOUSE_av_at(all_attrs, i) );
581 }
582}
441964ce 583
fcdc12ac 584void
ba153b33 585new_object(SV* meta, ...)
aa2d2e2c 586CODE:
587{
aa2d2e2c 588 AV* const xc = mouse_get_xc(aTHX_ meta);
074a414d 589 HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items);
fcdc12ac 590 SV* object;
d4712779 591
fcdc12ac 592 object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
593 mouse_class_initialize_object(aTHX_ meta, object, args, FALSE);
e0f85c26 594 mouse_buildall(aTHX_ xc, object, sv_2mortal(newRV_inc((SV*)args)));
fcdc12ac 595 ST(0) = object; /* because object is mortal, we should return it as is */
596 XSRETURN(1);
aa2d2e2c 597}
aa2d2e2c 598
fcdc12ac 599void
13b99908 600clone_object(SV* meta, SV* object, ...)
601CODE:
602{
603 AV* const xc = mouse_get_xc(aTHX_ meta);
604 HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax + 1, items - 1);
fcdc12ac 605 SV* proto;
13b99908 606
607 if(!mouse_is_an_instance_of(aTHX_ MOUSE_xc_stash(xc), object)) {
608 mouse_throw_error(meta, object,
609 "You must pass an instance of the metaclass (%"SVf"), not (%"SVf")",
610 mcall0(meta, mouse_name), object);
611 }
612
fcdc12ac 613 proto = mouse_instance_clone(aTHX_ object);
614 mouse_class_initialize_object(aTHX_ meta, proto, args, TRUE);
615 ST(0) = proto; /* because object is mortal, we should return it as is */
616 XSRETURN(1);
13b99908 617}
13b99908 618
aa2d2e2c 619void
cb6a9721 620_initialize_object(SV* meta, SV* object, HV* args, bool is_cloning = FALSE)
aa2d2e2c 621CODE:
622{
cb6a9721 623 mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning);
aa2d2e2c 624}
625
6f09819f 626void
627_invalidate_metaclass_cache(SV* meta)
628CODE:
629{
630 AV* const xc = mouse_get_xc_if_fresh(aTHX_ meta);
631 if(xc) {
632 SV* const gen = MOUSE_xc_gen(xc);
633 sv_setuv(gen, 0U);
634 }
635 delete_slot(meta, newSVpvs_flags("_mouse_cache_", SVs_TEMP));
636}
637
638
43165725 639MODULE = Mouse PACKAGE = Mouse::Meta::Role
640
641BOOT:
642 INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles);
643 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id);
644
787f84c2 645 INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method");
e058b279 646
cb60d0b5 647void
648add_before_modifier(SV* self, SV* name, SV* modifier)
649CODE:
650{
fe5044b1 651 av_push(mouse_get_modifier_storage(aTHX_ self, (enum mouse_modifier_t)ix, name), newSVsv(modifier));
cb60d0b5 652}
653ALIAS:
654 add_before_method_modifier = MOUSE_M_BEFORE
655 add_around_method_modifier = MOUSE_M_AROUND
656 add_after_method_modifier = MOUSE_M_AFTER
657
658void
659get_before_modifiers(SV* self, SV* name)
660ALIAS:
661 get_before_method_modifiers = MOUSE_M_BEFORE
662 get_around_method_modifiers = MOUSE_M_AROUND
663 get_after_method_modifiers = MOUSE_M_AFTER
664PPCODE:
665{
e0f85c26 666 AV* const storage = mouse_get_modifier_storage(aTHX_ self,
667 (enum mouse_modifier_t)ix, name);
cb60d0b5 668 I32 const len = av_len(storage) + 1;
669 if(GIMME_V == G_ARRAY) {
670 I32 i;
671 EXTEND(SP, len);
672 for(i = 0; i < len; i++){
673 PUSHs(*av_fetch(storage, i, TRUE));
674 }
675 }
676 else{
677 mPUSHi(len);
678 }
679}
680
b4dc9315 681void
682add_metaclass_accessor(SV* self, SV* name)
683CODE:
684{
4bc0a681 685 SV* const klass = mcall0(self, mouse_name);
b4dc9315 686 const char* fq_name = form("%"SVf"::%"SVf, klass, name);
687 STRLEN keylen;
688 const char* const key = SvPV_const(name, keylen);
689 mouse_simple_accessor_generate(aTHX_ fq_name, key, keylen,
690 XS_Mouse_inheritable_class_accessor, NULL, 0);
691}
692
aa2d2e2c 693MODULE = Mouse PACKAGE = Mouse::Object
43165725 694
fcdc12ac 695void
074a414d 696new(SV* klass, ...)
697CODE:
698{
1bbf8369 699 SV* const meta = mouse_initialize_metaclass(aTHX_ klass);
074a414d 700 AV* const xc = mouse_get_xc(aTHX_ meta);
701 UV const flags = MOUSE_xc_flags(xc);
702 SV* args;
fcdc12ac 703 SV* object;
074a414d 704
705 /* BUILDARGS */
706 if(flags & MOUSEf_XC_HAS_BUILDARGS){
adb5eb76 707 I32 i;
074a414d 708 SPAGAIN;
709
710 PUSHMARK(SP);
711 EXTEND(SP, items);
712 for(i = 0; i < items; i++){
713 PUSHs(ST(i));
714 }
b3cd4c14 715
074a414d 716 PUTBACK;
b3cd4c14 717 call_method_safes("BUILDARGS", G_SCALAR);
718
074a414d 719 SPAGAIN;
720 args = POPs;
721 PUTBACK;
722
723 if(!IsHashRef(args)){
724 croak("BUILDARGS did not return a HASH reference");
725 }
726 }
727 else{
728 args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items));
729 sv_2mortal(args);
730 }
731
732 /* new_object */
fcdc12ac 733 object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
734 mouse_class_initialize_object(aTHX_ meta, object, (HV*)SvRV(args), FALSE);
e0f85c26 735 /* BUILDALL */
736 mouse_buildall(aTHX_ xc, object, args);
fcdc12ac 737 ST(0) = object; /* because object is mortal, we should return it as is */
738 XSRETURN(1);
074a414d 739}
074a414d 740
a5c683f6 741void
742DESTROY(SV* object)
adb5eb76 743ALIAS:
744 DESTROY = 0
745 DEMOLISHALL = 1
aa2d2e2c 746CODE:
747{
a5c683f6 748 SV* const meta = get_metaclass(object);
bdf225bb 749 AV* xc;
a5c683f6 750 AV* demolishall;
bdf225bb 751 I32 len;
752 I32 i;
a5c683f6 753
754 if(!IsObject(object)){
31113a4a 755 croak("You must not call %s as a class method",
756 ix == 0 ? "DESTROY" : "DEMOLISHALL");
a5c683f6 757 }
758
bdf225bb 759 if(SvOK(meta) && (xc = mouse_get_xc_if_fresh(aTHX_ meta))) {
a5c683f6 760 demolishall = MOUSE_xc_demolishall(xc);
761 }
adb5eb76 762 else { /* The metaclass is already destroyed */
a5c683f6 763 AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object)));
7efbc77d 764
a5c683f6 765 len = AvFILLp(linearized_isa) + 1;
7efbc77d 766
a5c683f6 767 demolishall = newAV_mortal();
768 for(i = 0; i < len; i++){
769 SV* const klass = MOUSE_av_at(linearized_isa, i);
770 HV* const st = gv_stashsv(klass, TRUE);
771 GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE);
772 if(gv && GvCVu(gv)){
773 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
774 }
775 }
776 }
777
bdf225bb 778 len = AvFILLp(demolishall) + 1;
a5c683f6 779 if(len > 0){
5bc4e0be 780 SV* const in_global_destruction = boolSV(PL_dirty);
781 SAVEI32(PL_statusvalue); /* local $? */
9bd5683d 782 PL_statusvalue = 0;
70425827 783
a5c683f6 784 SAVESPTR(ERRSV); /* local $@ */
b3cd4c14 785 ERRSV = sv_newmortal();
786
787 EXTEND(SP, 2);
a5c683f6 788
a5c683f6 789 for(i = 0; i < len; i++){
f8beb873 790 SPAGAIN;
a5c683f6 791
792 PUSHMARK(SP);
b3cd4c14 793 PUSHs(object);
5bc4e0be 794 PUSHs(in_global_destruction);
a5c683f6 795 PUTBACK;
796
accc9fc2 797 call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL);
798
799 /* discard a scalar which G_VOID returns */
800 SPAGAIN;
801 (void)POPs;
802 PUTBACK;
803
6ad77996 804 if(sv_true(ERRSV)){
a5c683f6 805 SV* const e = newSVsv(ERRSV);
806
807 FREETMPS;
808 LEAVE;
809
810 sv_setsv(ERRSV, e);
811 SvREFCNT_dec(e);
812 croak(NULL); /* rethrow */
813 }
814 }
815 }
816}
817
818HV*
819BUILDARGS(SV* klass, ...)
7efbc77d 820CODE:
821{
a5c683f6 822 RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items);
7efbc77d 823}
824OUTPUT:
825 RETVAL
826
a5c683f6 827
adb5eb76 828void
829BUILDALL(SV* self, SV* args)
830CODE:
831{
f50e50d6 832 SV* const meta = get_metaclass(self);
833 AV* const xc = mouse_get_xc(aTHX_ meta);
adb5eb76 834
d06d9266 835 must_ref(args, "a HASH reference to BUILDALL", SVt_PVHV);
adb5eb76 836 mouse_buildall(aTHX_ xc, self, args);
837}