X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=xs-src%2FMouse.xs;h=5c7e623ae294478ff798e1bf0a2b23a144232309;hb=60cd9f19cf891bd7bff4ca33efa71bb3e95f0cb9;hp=b095d32a5b5dd50ca8e71761820a07124cb1e8e2;hpb=e128626c409797822ffd8a4079f833eb3dc0fd37;p=gitmo%2FMouse.git diff --git a/xs-src/Mouse.xs b/xs-src/Mouse.xs index b095d32..5c7e623 100644 --- a/xs-src/Mouse.xs +++ b/xs-src/Mouse.xs @@ -41,6 +41,12 @@ enum mouse_xc_ix_t{ MOUSE_XC_last }; +enum mouse_modifier_t { + MOUSE_M_BEFORE, + MOUSE_M_AROUND, + MOUSE_M_AFTER, +}; + static MGVTBL mouse_xc_vtbl; /* for identity */ static void @@ -285,10 +291,10 @@ mouse_report_unknown_args(pTHX_ SV* const meta, AV* const attrs, HV* const args) static void -mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) { +mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const is_cloning) { AV* const xc = mouse_get_xc(aTHX_ meta); AV* const attrs = MOUSE_xc_attrall(xc); - I32 len = AvFILLp(attrs) + 1; + I32 const len = AvFILLp(attrs) + 1; I32 i; AV* triggers_queue = NULL; I32 used = 0; @@ -301,10 +307,6 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const croak("You cannot use tied HASH reference as initializing arguments"); } - if(!ignore_triggers){ - triggers_queue = newAV_mortal(); - } - /* for each attribute */ for(i = 0; i < len; i++){ SV* const attr = MOUSE_av_at(attrs, i); @@ -324,16 +326,19 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){ weaken_slot(object, slot); } - if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){ + if(flags & MOUSEf_ATTR_HAS_TRIGGER){ AV* const pair = newAV(); av_push(pair, newSVsv( mcall0s(attr, "trigger") )); av_push(pair, newSVsv(value)); + if(!triggers_queue) { + triggers_queue = newAV_mortal(); + } av_push(triggers_queue, (SV*)pair); } used++; } - else { /* no init arg */ + else if(!is_cloning){ /* no init arg, noop while cloning */ if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){ if(!(flags & MOUSEf_ATTR_IS_LAZY)){ mouse_xa_set_default(aTHX_ xa, object); @@ -350,7 +355,7 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const } if(triggers_queue){ - len = AvFILLp(triggers_queue) + 1; + I32 const len = AvFILLp(triggers_queue) + 1; for(i = 0; i < len; i++){ AV* const pair = (AV*)AvARRAY(triggers_queue)[i]; SV* const trigger = AvARRAY(pair)[0]; @@ -371,18 +376,7 @@ mouse_initialize_metaclass(pTHX_ SV* const klass) { SV* meta = get_metaclass(klass); if(!SvOK(meta)){ - dSP; - PUSHMARK(SP); - - EXTEND(SP, 2); - mPUSHp("Mouse::Meta::Class", sizeof("Mouse::Meta::Class")-1); - PUSHs(klass); - PUTBACK; - - call_method("initialize", G_SCALAR); - SPAGAIN; - meta = POPs; - PUTBACK; + meta = mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP), "initialize", klass); } return meta; @@ -402,7 +396,7 @@ mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { PUSHs(args); PUTBACK; - call_sv(AvARRAY(buildall)[i], G_VOID); + call_sv_safe(AvARRAY(buildall)[i], G_VOID); /* discard a scalar which G_VOID returns */ SPAGAIN; @@ -411,6 +405,44 @@ mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { } } +static AV* +mouse_get_modifier_storage(pTHX_ + SV* const meta, + enum mouse_modifier_t const m, SV* const name) { + static const char* const keys[] = { + "before", + "around", + "after", + }; + SV* const key = sv_2mortal(Perl_newSVpvf(aTHX_ "%s_method_modifiers", keys[m])); + SV* table; + SV* storage_ref; + + must_defined(name, "a method name"); + + table = get_slot(meta, key); + + if(!table){ + /* $meta->{$key} = {} */ + table = sv_2mortal(newRV_noinc((SV*)newHV())); + set_slot(meta, key, table); + } + + storage_ref = get_slot(table, name); + + if(!storage_ref){ + storage_ref = sv_2mortal(newRV_noinc((SV*)newAV())); + set_slot(table, name, storage_ref); + } + else{ + if(!IsArrayRef(storage_ref)){ + croak("Modifier strorage for '%s' is not an ARRAY reference", keys[m]); + } + } + + return (AV*)SvRV(storage_ref); +} + MODULE = Mouse PACKAGE = Mouse PROTOTYPES: DISABLE @@ -465,65 +497,23 @@ CODE: croak("No package name defined"); } - SvGETMAGIC(name); - SvGETMAGIC(code); - - if(!SvOK(name)){ - mouse_throw_error(self, NULL, "You must define a method name"); - } - if(!SvROK(code)){ - mouse_throw_error(self, NULL, "You must define a CODE reference"); - } + must_defined(name, "a method name"); + must_ref (code, "a CODE reference", SVt_NULL); /* any reftype is OK */ code_ref = code; if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){ SV* sv = code_ref; /* used in tryAMAGICunDEREF */ SV** sp = &sv; /* used in tryAMAGICunDEREF */ tryAMAGICunDEREF(to_cv); /* try \&{$code} */ - if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)){ - mouse_throw_error(self, NULL, "You must pass a CODE reference to add_method"); - } + must_ref(code, "a CODE reference", SVt_PVCV); code_ref = sv; } /* *{$package . '::' . $name} -> *gv */ gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV); - if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */ - SvREFCNT_dec(GvCV(gv)); - GvCV(gv) = NULL; - } - sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */ - + mouse_install_sub(aTHX_ gv, code_ref); + //CvMETHOD_on((CV*)SvRV(code_ref)); (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ - - /* name the CODE ref if it's anonymous */ - { - CV* const code_entity = (CV*)SvRV(code_ref); - if(CvANON(code_entity) - && CvGV(code_entity) /* a cv under construction has no gv */ ){ - HV* dbsub; - - /* update %DB::sub to make NYTProf happy */ - if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON)) - && PL_DBsub && (dbsub = GvHV(PL_DBsub)) - ){ - /* see Perl_newATTRSUB() in op.c */ - SV* const subname = sv_newmortal(); - HE* orig; - - gv_efullname3(subname, CvGV(code_entity), NULL); - orig = hv_fetch_ent(dbsub, subname, FALSE, 0U); - if(orig){ - gv_efullname3(subname, gv, NULL); - (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U); - SvREFCNT_inc_simple_void_NN(HeVAL(orig)); - } - } - - CvGV(code_entity) = gv; - CvANON_off(code_entity); - } - } } MODULE = Mouse PACKAGE = Mouse::Meta::Class @@ -593,10 +583,10 @@ OUTPUT: RETVAL void -_initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE) +_initialize_object(SV* meta, SV* object, HV* args, bool is_cloning = FALSE) CODE: { - mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers); + mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning); } MODULE = Mouse PACKAGE = Mouse::Meta::Role @@ -607,6 +597,39 @@ BOOT: INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method"); +void +add_before_modifier(SV* self, SV* name, SV* modifier) +CODE: +{ + av_push(mouse_get_modifier_storage(aTHX_ self, ix, name), newSVsv(modifier)); +} +ALIAS: + add_before_method_modifier = MOUSE_M_BEFORE + add_around_method_modifier = MOUSE_M_AROUND + add_after_method_modifier = MOUSE_M_AFTER + +void +get_before_modifiers(SV* self, SV* name) +ALIAS: + get_before_method_modifiers = MOUSE_M_BEFORE + get_around_method_modifiers = MOUSE_M_AROUND + get_after_method_modifiers = MOUSE_M_AFTER +PPCODE: +{ + AV* const storage = mouse_get_modifier_storage(aTHX_ self, ix, name); + I32 const len = av_len(storage) + 1; + if(GIMME_V == G_ARRAY) { + I32 i; + EXTEND(SP, len); + for(i = 0; i < len; i++){ + PUSHs(*av_fetch(storage, i, TRUE)); + } + } + else{ + mPUSHi(len); + } +} + MODULE = Mouse PACKAGE = Mouse::Object SV* @@ -628,9 +651,10 @@ CODE: for(i = 0; i < items; i++){ PUSHs(ST(i)); } - //SP += items; + PUTBACK; - call_method("BUILDARGS", G_SCALAR); + call_method_safes("BUILDARGS", G_SCALAR); + SPAGAIN; args = POPs; PUTBACK; @@ -692,18 +716,21 @@ CODE: len = AvFILLp(demolishall) + 1; if(len > 0){ - GV* const statusvalue = gv_fetchpvs("?", 0, SVt_PV); - SAVESPTR(GvSV(statusvalue)); /* local $? */ + SV* const in_global_destruction = boolSV(PL_dirty); + SAVEI32(PL_statusvalue); /* local $? */ + PL_statusvalue = 0; + SAVESPTR(ERRSV); /* local $@ */ + ERRSV = sv_newmortal(); + + EXTEND(SP, 2); - GvSV(statusvalue) = sv_newmortal(); - ERRSV = newSVpvs_flags("", SVs_TEMP); for(i = 0; i < len; i++){ SPAGAIN; PUSHMARK(SP); - XPUSHs(object); - XPUSHs(boolSV(PL_dirty)); + PUSHs(object); + PUSHs(in_global_destruction); PUTBACK; call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL); @@ -744,8 +771,6 @@ CODE: SV* const meta = get_metaclass(self); AV* const xc = mouse_get_xc(aTHX_ meta); - if(!IsHashRef(args)){ - croak("You must pass a HASH reference to BUILDALL"); - } + must_ref(args, "a HASH reference to BUILDALL", SVt_PVHV); mouse_buildall(aTHX_ xc, self, args); }