Make add_method update %DB::sub for profilers
[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,
24
25 MOUSEf_XC_mask = 0xFFFF /* not used */
26};
27
a39e9541 28/* Mouse XS Metaclass object */
29enum mouse_xc_ix_t{
4e7e3250 30 MOUSE_XC_FLAGS,
31
a39e9541 32 MOUSE_XC_GEN, /* class generation */
aa2d2e2c 33 MOUSE_XC_STASH, /* symbol table hash */
aa2d2e2c 34
a39e9541 35 MOUSE_XC_ATTRALL, /* all the attributes */
36 MOUSE_XC_BUILDALL, /* all the BUILD methods */
37 MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */
38
39 MOUSE_XC_last
40};
41
42static MGVTBL mouse_xc_vtbl; /* for identity */
43
44static void
45mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){
46 dSP;
47 I32 n;
48
49 /* $meta->get_attribute_list */
50 PUSHMARK(SP);
51 XPUSHs(metaclass);
52 PUTBACK;
53
047d7af0 54 n = call_sv(mouse_get_attribute_list, G_ARRAY | G_METHOD);
a39e9541 55 for(NOOP; n > 0; n--){
56 SV* name;
57
58 SPAGAIN;
59 name = POPs;
60 PUTBACK;
61
62 if(hv_exists_ent(seen, name, 0U)){
63 continue;
64 }
65 (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
da4432f3 66
047d7af0 67 av_push(attrall, newSVsv( mcall1(metaclass, mouse_get_attribute, name) ));
a39e9541 68 }
69}
743ca82e 70
aa2d2e2c 71static int
72mouse_class_has_custom_buildargs(pTHX_ HV* const stash) {
73 XS(XS_Mouse__Object_BUILDARGS); /* prototype */
74
75 GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0);
aa2d2e2c 76
12872cc1 77 return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS;
aa2d2e2c 78}
79
a39e9541 80static void
81mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) {
82 AV* const linearized_isa = mro_get_linear_isa(stash);
83 I32 const len = AvFILLp(linearized_isa);
84 I32 i;
aa2d2e2c 85 U32 flags = 0x00;
a39e9541 86 AV* const attrall = newAV();
87 AV* const buildall = newAV();
88 AV* const demolishall = newAV();
89 HV* const seen = newHV(); /* for attributes */
da4432f3 90
a39e9541 91 ENTER;
92 SAVETMPS;
da4432f3 93
a39e9541 94 sv_2mortal((SV*)seen);
da4432f3 95
a39e9541 96 /* old data will be delete at the end of the perl scope */
97 av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00);
98 av_delete(xc, MOUSE_XC_BUILDALL, 0x00);
99 av_delete(xc, MOUSE_XC_ATTRALL, 0x00);
743ca82e 100
a39e9541 101 SvREFCNT_inc_simple_void_NN(linearized_isa);
102 sv_2mortal((SV*)linearized_isa);
da4432f3 103
a39e9541 104 /* update */
da4432f3 105
4e7e3250 106 if(predicate_calls(metaclass, "is_immutable")){
aa2d2e2c 107 flags |= MOUSEf_XC_IS_IMMUTABLE;
108 }
109
4e7e3250 110 if(predicate_calls(metaclass, "is_anon_class")){
111 flags |= MOUSEf_XC_IS_ANON;
112 }
113
aa2d2e2c 114 if(mouse_class_has_custom_buildargs(aTHX_ stash)){
115 flags |= MOUSEf_XC_HAS_BUILDARGS;
116 }
117
4e7e3250 118 av_store(xc, MOUSE_XC_FLAGS, newSVuv(flags));
a39e9541 119 av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall);
120 av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall);
121 av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
da4432f3 122
a39e9541 123 for(i = 0; i < len; i++){
047d7af0 124 SV* const klass = MOUSE_av_at(linearized_isa, i);
074a414d 125 HV* const st = gv_stashsv(klass, TRUE);
a39e9541 126 SV* meta;
127 GV* gv;
da4432f3 128
074a414d 129 gv = stash_fetchs(st, "BUILD", FALSE);
a39e9541 130 if(gv && GvCVu(gv)){
074a414d 131 av_unshift(buildall, 1);
132 av_store(buildall, 0, newRV_inc((SV*)GvCV(gv)));
a39e9541 133 }
da4432f3 134
074a414d 135 gv = stash_fetchs(st, "DEMOLISH", FALSE);
a39e9541 136 if(gv && GvCVu(gv)){
137 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
138 }
da4432f3 139
a39e9541 140 /* ATTRIBUTES */
aa2d2e2c 141 meta = get_metaclass(klass);
a39e9541 142 if(!SvOK(meta)){
143 continue; /* skip non-Mouse classes */
144 }
da4432f3 145
a39e9541 146 mouse_class_push_attribute_list(aTHX_ meta, attrall, seen);
147 }
da4432f3 148
a39e9541 149 FREETMPS;
150 LEAVE;
da4432f3 151
a39e9541 152 sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash));
153}
da4432f3 154
3001d9ac 155static AV*
a39e9541 156mouse_get_xc(pTHX_ SV* const metaclass) {
157 AV* xc;
158 SV* gen;
159 HV* stash;
160 MAGIC* mg;
161
162 if(!IsObject(metaclass)){
163 croak("Not a Mouse metaclass");
164 }
da4432f3 165
a39e9541 166 mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00);
167 if(!mg){
168 SV* const package = get_slot(metaclass, mouse_package);
aa2d2e2c 169 STRLEN len;
170 const char* const pv = SvPV_const(package, len);
da4432f3 171
aa2d2e2c 172 stash = gv_stashpvn(pv, len, TRUE);
a39e9541 173 xc = newAV();
da4432f3 174
aa2d2e2c 175 mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, &mouse_xc_vtbl, pv, len);
a39e9541 176 SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */
da4432f3 177
a39e9541 178 av_extend(xc, MOUSE_XC_last - 1);
aa2d2e2c 179
a5c683f6 180 av_store(xc, MOUSE_XC_GEN, newSVuv(0U));
aa2d2e2c 181 av_store(xc, MOUSE_XC_STASH, (SV*)stash);
4e7e3250 182
aa2d2e2c 183 SvREFCNT_inc_simple_void_NN(stash);
da4432f3 184 }
a39e9541 185 else{
a39e9541 186 xc = (AV*)MOUSE_mg_obj(mg);
da4432f3 187
a39e9541 188 assert(xc);
189 assert(SvTYPE(xc) == SVt_PVAV);
190 }
da4432f3 191
aa2d2e2c 192 gen = MOUSE_xc_gen(xc);
a5c683f6 193
194 if(SvUVX(gen) != 0U && MOUSE_xc_flags(xc) & MOUSEf_XC_IS_IMMUTABLE){
195 return xc;
196 }
197
aa2d2e2c 198 stash = MOUSE_xc_stash(xc);
199
a5c683f6 200 if(SvUVX(gen) != mro_get_pkg_gen(stash)){
a39e9541 201 mouse_class_update_xc(aTHX_ metaclass, stash, xc);
da4432f3 202 }
a39e9541 203
204 return xc;
205}
206
3001d9ac 207static HV*
074a414d 208mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) {
aa2d2e2c 209 HV* args;
074a414d 210
211 /* shift @_ */
212 ax++;
213 items--;
214
215 if(items == 1){
216 SV* const args_ref = ST(0);
aa2d2e2c 217 if(!IsHashRef(args_ref)){
218 if(!metaclass){ metaclass = get_metaclass(klass); }
219 mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref");
220 }
221 args = newHVhv((HV*)SvRV(args_ref));
222 sv_2mortal((SV*)args);
223 }
224 else{
225 I32 i;
226
6fab29c6 227 args = newHV_mortal();
aa2d2e2c 228
074a414d 229 if( (items % 2) != 0 ){
aa2d2e2c 230 if(!metaclass){ metaclass = get_metaclass(klass); }
231 mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()");
232 }
233
074a414d 234 for(i = 0; i < items; i += 2){
aa2d2e2c 235 (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U);
236 }
237
238 }
239 return args;
240}
241
3001d9ac 242static void
d4712779 243mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) {
244 AV* const xc = mouse_get_xc(aTHX_ meta);
245 AV* const attrs = MOUSE_xc_attrall(xc);
4e7e3250 246 I32 len = AvFILLp(attrs) + 1;
d4712779 247 I32 i;
4e7e3250 248 AV* triggers_queue = NULL;
249
074a414d 250 assert(meta || object);
251 assert(args);
252 assert(SvTYPE(args) == SVt_PVHV);
253
6463e839 254 if(mg_find((SV*)args, PERL_MAGIC_tied)){
255 croak("You cannot use tied HASH reference as initializing arguments");
256 }
257
4e7e3250 258 if(!ignore_triggers){
259 triggers_queue = newAV_mortal();
260 }
0aad0266 261
d4712779 262 for(i = 0; i < len; i++){
6bc5b495 263 SV* const attr = MOUSE_av_at(attrs, i);
264 AV* const xa = mouse_get_xa(aTHX_ attr);
4e7e3250 265
266 SV* const slot = MOUSE_xa_slot(xa);
267 U16 const flags = (U16)MOUSE_xa_flags(xa);
268 SV* const init_arg = MOUSE_xa_init_arg(xa);
269 HE* he;
270
271 if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){
272 SV* value = HeVAL(he);
273 if(flags & MOUSEf_ATTR_HAS_TC){
274 value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
275 }
ca8e67d6 276 value = set_slot(object, slot, value);
4e7e3250 277 if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){
278 weaken_slot(object, slot);
279 }
280 if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){
281 AV* const pair = newAV();
282 av_push(pair, newSVsv( mcall0s(attr, "trigger") ));
283 av_push(pair, newSVsv(value));
284
285 av_push(triggers_queue, (SV*)pair);
286 }
287 }
288 else { /* no init arg */
289 if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){
290 if(!(flags & MOUSEf_ATTR_IS_LAZY)){
291 mouse_xa_set_default(aTHX_ xa, object);
292 }
293 }
294 else if(flags & MOUSEf_ATTR_IS_REQUIRED) {
295 mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot);
296 }
297 }
298 } /* for each attributes */
299
300 if(triggers_queue){
301 len = AvFILLp(triggers_queue) + 1;
302 for(i = 0; i < len; i++){
303 AV* const pair = (AV*)AvARRAY(triggers_queue)[i];
304 SV* const trigger = AvARRAY(pair)[0];
305 SV* const value = AvARRAY(pair)[1];
306
307 mcall1(object, trigger, value);
308 }
309 }
310
311 if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
ca8e67d6 312 (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta);
d4712779 313 }
4e7e3250 314
aa2d2e2c 315}
316
a5c683f6 317static SV*
1bbf8369 318mouse_initialize_metaclass(pTHX_ SV* const klass) {
319 SV* meta = get_metaclass(klass);
320
321 if(!SvOK(meta)){
322 dSP;
323 PUSHMARK(SP);
324
325 EXTEND(SP, 2);
326 mPUSHp("Mouse::Meta::Class", sizeof("Mouse::Meta::Class")-1);
327 PUSHs(klass);
328 PUTBACK;
329
330 call_method("initialize", G_SCALAR);
331 SPAGAIN;
332 meta = POPs;
333 PUTBACK;
334 }
335
336 return meta;
337}
338
adb5eb76 339static void
340mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) {
341 AV* const buildall = MOUSE_xc_buildall(xc);
342 I32 const len = AvFILLp(buildall) + 1;
343 I32 i;
344 for(i = 0; i < len; i++){
345 dSP;
346
347 PUSHMARK(SP);
348 EXTEND(SP, 2);
349 PUSHs(object);
350 PUSHs(args);
351 PUTBACK;
352
accc9fc2 353 call_sv(AvARRAY(buildall)[i], G_VOID);
354
355 /* discard a scalar which G_VOID returns */
356 SPAGAIN;
357 (void)POPs;
358 PUTBACK;
adb5eb76 359 }
360}
361
646c0371 362MODULE = Mouse PACKAGE = Mouse
df6dd016 363
364PROTOTYPES: DISABLE
365
cccb83de 366BOOT:
367 mouse_package = newSVpvs_share("package");
368 mouse_namespace = newSVpvs_share("namespace");
3e44140b 369 mouse_methods = newSVpvs_share("methods");
a5df48e5 370 mouse_name = newSVpvs_share("name");
d39ac61c 371 mouse_coerce = newSVpvs_share("coerce");
3e44140b 372
047d7af0 373 mouse_get_attribute = newSVpvs_share("get_attribute");
374 mouse_get_attribute_list = newSVpvs_share("get_attribute_list");
da4432f3 375
646c0371 376 MOUSE_CALL_BOOT(Mouse__Util);
1d5ecd5f 377 MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints);
646c0371 378 MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS);
aa2d2e2c 379 MOUSE_CALL_BOOT(Mouse__Meta__Attribute);
f48920c1 380
cccb83de 381
7d96ae4d 382MODULE = Mouse PACKAGE = Mouse::Meta::Module
383
384BOOT:
385 INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package);
386 INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods);
387 INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes);
388
389HV*
390namespace(SV* self)
391CODE:
392{
6a97bbda 393 SV* const package = get_slot(self, mouse_package);
7d96ae4d 394 if(!(package && SvOK(package))){
3e44140b 395 croak("No package name defined");
7d96ae4d 396 }
397 RETVAL = gv_stashsv(package, GV_ADDMULTI);
398}
399OUTPUT:
400 RETVAL
401
3e44140b 402# ignore extra arguments for extensibility
403void
404add_method(SV* self, SV* name, SV* code, ...)
405CODE:
406{
6a97bbda 407 SV* const package = get_slot(self, mouse_package); /* $self->{package} */
408 SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */
3e44140b 409 GV* gv;
410 SV* code_ref;
411
412 if(!(package && SvOK(package))){
413 croak("No package name defined");
414 }
415
416 SvGETMAGIC(name);
417 SvGETMAGIC(code);
418
419 if(!SvOK(name)){
420 mouse_throw_error(self, NULL, "You must define a method name");
421 }
422 if(!SvROK(code)){
423 mouse_throw_error(self, NULL, "You must define a CODE reference");
424 }
425
426 code_ref = code;
427 if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){
428 SV* sv = code_ref; /* used in tryAMAGICunDEREF */
429 SV** sp = &sv; /* used in tryAMAGICunDEREF */
430 tryAMAGICunDEREF(to_cv); /* try \&{$code} */
24d5564d 431 if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)){
432 mouse_throw_error(self, NULL, "You must pass a CODE reference to add_method");
3e44140b 433 }
434 code_ref = sv;
435 }
436
437 /* *{$package . '::' . $name} -> *gv */
438 gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV);
439 if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */
440 SvREFCNT_dec(GvCV(gv));
441 GvCV(gv) = NULL;
442 }
443 sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */
444
ca8e67d6 445 (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */
3e44140b 446
855eff0e 447 /* name the CODE ref if it's anonymous */
448 {
449 CV* const code_entity = (CV*)SvRV(code_ref);
450 if(CvANON(code_entity)
451 && CvGV(code_entity) /* a cv under construction has no gv */ ){
40e57f0a 452 HV* dbsub;
453
454 /* update %DB::sub to make NYTProf happy */
455 if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON))
456 && PL_DBsub && (dbsub = GvHV(PL_DBsub))
457 ){
458 /* see Perl_newATTRSUB() in op.c */
459 SV* const subname = sv_newmortal();
460 HE* orig;
461
462 gv_efullname3(subname, CvGV(code_entity), NULL);
463 orig = hv_fetch_ent(dbsub, subname, FALSE, 0U);
464 if(orig){
465 gv_efullname3(subname, gv, NULL);
466 (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U);
467 SvREFCNT_inc_simple_void_NN(HeVAL(orig));
468 }
469 }
855eff0e 470
471 CvGV(code_entity) = gv;
472 CvANON_off(code_entity);
473 }
474 }
3e44140b 475}
476
43165725 477MODULE = Mouse PACKAGE = Mouse::Meta::Class
478
479BOOT:
480 INSTALL_SIMPLE_READER(Class, roles);
481 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
80efe911 482 INSTALL_SIMPLE_READER(Class, is_immutable);
a5c683f6 483
787f84c2 484 INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method");
485 INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute");
486 INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS");
487 INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS");
a5c683f6 488
489 newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Constructor::XS", TRUE), "_generate_constructor",
490 newRV_inc((SV*)get_cvs("Mouse::Object::new", TRUE)));
491 newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Destructor::XS", TRUE), "_generate_destructor",
492 newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", TRUE)));
493
43165725 494
cccb83de 495void
496linearized_isa(SV* self)
497PPCODE:
498{
499 SV* const stash_ref = mcall0(self, mouse_namespace); /* $self->namespace */
500 AV* linearized_isa;
501 I32 len;
502 I32 i;
503 if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)){
504 croak("namespace() didn't return a HASH reference");
505 }
506 linearized_isa = mro_get_linear_isa((HV*)SvRV(stash_ref));
507 len = AvFILLp(linearized_isa) + 1;
508 EXTEND(SP, len);
509 for(i = 0; i < len; i++){
510 PUSHs(AvARRAY(linearized_isa)[i]);
511 }
512}
513
da4432f3 514void
515get_all_attributes(SV* self)
516PPCODE:
517{
0aad0266 518 AV* const xc = mouse_get_xc(aTHX_ self);
ae34e077 519 AV* const all_attrs = MOUSE_xc_attrall(xc);
da4432f3 520 I32 const len = AvFILLp(all_attrs) + 1;
521 I32 i;
522
523 EXTEND(SP, len);
524 for(i = 0; i < len; i++){
525 PUSHs( MOUSE_av_at(all_attrs, i) );
526 }
527}
441964ce 528
aa2d2e2c 529SV*
ba153b33 530new_object(SV* meta, ...)
aa2d2e2c 531CODE:
532{
aa2d2e2c 533 AV* const xc = mouse_get_xc(aTHX_ meta);
074a414d 534 HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items);
d4712779 535
aa2d2e2c 536 RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
d4712779 537 mouse_class_initialize_object(aTHX_ meta, RETVAL, args, FALSE);
aa2d2e2c 538}
ba153b33 539OUTPUT:
540 RETVAL
aa2d2e2c 541
542void
4e7e3250 543_initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE)
aa2d2e2c 544CODE:
545{
d4712779 546 mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers);
aa2d2e2c 547}
548
43165725 549MODULE = Mouse PACKAGE = Mouse::Meta::Role
550
551BOOT:
552 INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles);
553 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id);
554
787f84c2 555 INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method");
e058b279 556
aa2d2e2c 557MODULE = Mouse PACKAGE = Mouse::Object
43165725 558
074a414d 559SV*
560new(SV* klass, ...)
561CODE:
562{
1bbf8369 563 SV* const meta = mouse_initialize_metaclass(aTHX_ klass);
074a414d 564 AV* const xc = mouse_get_xc(aTHX_ meta);
565 UV const flags = MOUSE_xc_flags(xc);
566 SV* args;
074a414d 567
568 /* BUILDARGS */
569 if(flags & MOUSEf_XC_HAS_BUILDARGS){
adb5eb76 570 I32 i;
074a414d 571 SPAGAIN;
572
573 PUSHMARK(SP);
574 EXTEND(SP, items);
575 for(i = 0; i < items; i++){
576 PUSHs(ST(i));
577 }
578 //SP += items;
579 PUTBACK;
580 call_method("BUILDARGS", G_SCALAR);
581 SPAGAIN;
582 args = POPs;
583 PUTBACK;
584
585 if(!IsHashRef(args)){
586 croak("BUILDARGS did not return a HASH reference");
587 }
588 }
589 else{
590 args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items));
591 sv_2mortal(args);
592 }
593
594 /* new_object */
595 RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc));
596 mouse_class_initialize_object(aTHX_ meta, RETVAL, (HV*)SvRV(args), FALSE);
597
adb5eb76 598 mouse_buildall(aTHX_ xc, RETVAL, args);
074a414d 599}
600OUTPUT:
601 RETVAL
602
a5c683f6 603void
604DESTROY(SV* object)
adb5eb76 605ALIAS:
606 DESTROY = 0
607 DEMOLISHALL = 1
aa2d2e2c 608CODE:
609{
a5c683f6 610 SV* const meta = get_metaclass(object);
611 AV* demolishall;
612 I32 len, i;
613
614 if(!IsObject(object)){
31113a4a 615 croak("You must not call %s as a class method",
616 ix == 0 ? "DESTROY" : "DEMOLISHALL");
a5c683f6 617 }
618
619 if(SvOK(meta)){
620 AV* const xc = mouse_get_xc(aTHX_ meta);
621
622 demolishall = MOUSE_xc_demolishall(xc);
623 }
adb5eb76 624 else { /* The metaclass is already destroyed */
a5c683f6 625 AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object)));
7efbc77d 626
a5c683f6 627 len = AvFILLp(linearized_isa) + 1;
7efbc77d 628
a5c683f6 629 demolishall = newAV_mortal();
630 for(i = 0; i < len; i++){
631 SV* const klass = MOUSE_av_at(linearized_isa, i);
632 HV* const st = gv_stashsv(klass, TRUE);
633 GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE);
634 if(gv && GvCVu(gv)){
635 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
636 }
637 }
638 }
639
a5c683f6 640 len = AvFILLp(demolishall) + 1;
641 if(len > 0){
642 GV* const statusvalue = gv_fetchpvs("?", 0, SVt_PV);
643 SAVESPTR(GvSV(statusvalue)); /* local $? */
644 SAVESPTR(ERRSV); /* local $@ */
645
f8beb873 646 GvSV(statusvalue) = sv_newmortal();
647 ERRSV = newSVpvs_flags("", SVs_TEMP);
a5c683f6 648 for(i = 0; i < len; i++){
f8beb873 649 SPAGAIN;
a5c683f6 650
651 PUSHMARK(SP);
652 XPUSHs(object);
653 PUTBACK;
654
accc9fc2 655 call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL);
656
657 /* discard a scalar which G_VOID returns */
658 SPAGAIN;
659 (void)POPs;
660 PUTBACK;
661
6ad77996 662 if(sv_true(ERRSV)){
a5c683f6 663 SV* const e = newSVsv(ERRSV);
664
665 FREETMPS;
666 LEAVE;
667
668 sv_setsv(ERRSV, e);
669 SvREFCNT_dec(e);
670 croak(NULL); /* rethrow */
671 }
672 }
673 }
674}
675
676HV*
677BUILDARGS(SV* klass, ...)
7efbc77d 678CODE:
679{
a5c683f6 680 RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items);
7efbc77d 681}
682OUTPUT:
683 RETVAL
684
a5c683f6 685
adb5eb76 686void
687BUILDALL(SV* self, SV* args)
688CODE:
689{
f50e50d6 690 SV* const meta = get_metaclass(self);
691 AV* const xc = mouse_get_xc(aTHX_ meta);
adb5eb76 692
693 if(!IsHashRef(args)){
694 croak("You must pass a HASH reference to BUILDALL");
695 }
adb5eb76 696 mouse_buildall(aTHX_ xc, self, args);
697}