X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=xs-src%2FMouseTypeConstraints.xs;h=a6e81eedef15d91d43e2d935b18f517a09fec480;hb=f50e50d653ac9fbdc9aeeff13bbf5456e603a339;hp=6f69f2fdc03ae357c5e018bb0f924d2f80d93b78;hpb=80aa5731d6763d8f38ba0fa057729af7026e6580;p=gitmo%2FMouse.git diff --git a/xs-src/MouseTypeConstraints.xs b/xs-src/MouseTypeConstraints.xs index 6f69f2f..a6e81ee 100644 --- a/xs-src/MouseTypeConstraints.xs +++ b/xs-src/MouseTypeConstraints.xs @@ -84,7 +84,7 @@ mouse_tc_Bool(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) { } } else{ - /* false must be boolean */ + /* any false value must be boolean */ return TRUE; } } @@ -149,21 +149,11 @@ mouse_tc_RoleName(pTHX_ SV* const data PERL_UNUSED_DECL, SV* const sv) { assert(sv); if(is_class_loaded(sv)){ int ok; - SV* meta; - dSP; ENTER; SAVETMPS; - PUSHMARK(SP); - XPUSHs(sv); - PUTBACK; - call_pv("Mouse::Util::get_metaclass_by_name", G_SCALAR); - SPAGAIN; - meta = POPs; - PUTBACK; - - ok = is_an_instance_of("Mouse::Meta::Role", meta); + ok = is_an_instance_of("Mouse::Meta::Role", get_metaclass(sv)); FREETMPS; LEAVE; @@ -271,6 +261,7 @@ mouse_parameterized_HashRef(pTHX_ SV* const param, SV* const sv) { SV* const value = hv_iterval(hv, he); SvGETMAGIC(value); if(!mouse_tc_check(aTHX_ param, value)){ + hv_iterinit(hv); /* reset */ return FALSE; } } @@ -329,6 +320,7 @@ mouse_types_check(pTHX_ AV* const types, SV* const sv) { #define MY_CXT_KEY "Mouse::Util::TypeConstraints::_guts" XS_VERSION typedef struct sui_cxt{ GV* universal_isa; + GV* universal_can; } my_cxt_t; START_MY_CXT @@ -368,6 +360,19 @@ mouse_lookup_isa(pTHX_ HV* const instance_stash, const char* const klass_pv){ return FALSE; } +#define find_method_pvn(a, b, c) mouse_stash_find_method(aTHX_ a, b, c) +#define find_method_pvs(a, b) mouse_stash_find_method(aTHX_ a, STR_WITH_LEN(b)) + +static inline GV* +mouse_stash_find_method(pTHX_ HV* const stash, const char* const name, I32 const namelen){ + GV** const gvp = (GV**)hv_fetch(stash, name, namelen, FALSE); + if(gvp && isGV(*gvp) && GvCV(*gvp)){ /* shortcut */ + return *gvp; + } + + return gv_fetchmeth_autoload(stash, name, namelen, 0); +} + int mouse_is_an_instance_of(pTHX_ HV* const stash, SV* const instance){ assert(stash); @@ -376,7 +381,7 @@ mouse_is_an_instance_of(pTHX_ HV* const stash, SV* const instance){ if(IsObject(instance)){ dMY_CXT; HV* const instance_stash = SvSTASH(SvRV(instance)); - GV* const instance_isa = gv_fetchmeth_autoload(instance_stash, "isa", sizeof("isa")-1, 0); + GV* const instance_isa = find_method_pvs(instance_stash, "isa"); /* the instance has no own isa method */ if(instance_isa == NULL || GvCV(instance_isa) == GvCV(MY_CXT.universal_isa)){ @@ -420,6 +425,56 @@ mouse_is_an_instance_of_universal(pTHX_ SV* const data, SV* const sv){ return SvROK(sv) && SvOBJECT(SvRV(sv)); } +static int +mouse_can_methods(pTHX_ AV* const methods, SV* const instance){ + if(IsObject(instance)){ + dMY_CXT; + HV* const mystash = SvSTASH(SvRV(instance)); + GV* const mycan = find_method_pvs(mystash, "can"); + bool const use_builtin = (mycan == NULL || GvCV(mycan) == GvCV(MY_CXT.universal_can)) ? TRUE : FALSE; + I32 const len = AvFILLp(methods) + 1; + I32 i; + for(i = 0; i < len; i++){ + SV* const name = MOUSE_av_at(methods, i); + + if(use_builtin){ + if(!find_method_pvn(mystash, SvPVX(name), SvCUR(name))){ + return FALSE; + } + } + else{ + bool ok; + dSP; + + ENTER; + SAVETMPS; + + PUSHMARK(SP); + EXTEND(SP, 2); + PUSHs(instance); + PUSHs(sv_mortalcopy(name)); + PUTBACK; + + call_method("can", G_SCALAR); + + SPAGAIN; + ok = SvTRUE(TOPs); + (void)POPs; + PUTBACK; + + FREETMPS; + LEAVE; + + if(!ok){ + return FALSE; + } + } + } + return TRUE; + } + return FALSE; +} + static MGVTBL mouse_util_type_constraints_vtbl; /* not used, only for identity */ static CV* @@ -465,6 +520,32 @@ mouse_generate_isa_predicate_for(pTHX_ SV* const klass, const char* const predic return mouse_tc_generate(aTHX_ predicate_name, fptr, param); } +CV* +mouse_generate_can_predicate_for(pTHX_ SV* const methods, const char* const predicate_name){ + AV* av; + AV* const param = newAV_mortal(); + I32 len; + I32 i; + + SvGETMAGIC(methods); + if(!IsArrayRef(methods)){ + croak("You must pass an ARRAY ref method names"); + } + av = (AV*)SvRV(methods); + + len = av_len(av) + 1; + for(i = 0; i < len; i++){ + SV* const name = *av_fetch(av, i, TRUE); + STRLEN pvlen; + const char* const pv = SvPV_const(name, pvlen); + + av_push(param, newSVpvn_share(pv, pvlen, 0U)); + } + + return mouse_tc_generate(aTHX_ predicate_name, (check_fptr_t)mouse_can_methods, (SV*)param); +} + + XS(XS_Mouse_constraint_check) { dVAR; dXSARGS; @@ -483,6 +564,9 @@ static void setup_my_cxt(pTHX_ pMY_CXT){ MY_CXT.universal_isa = gv_fetchpvs("UNIVERSAL::isa", GV_ADD, SVt_PVCV); SvREFCNT_inc_simple_void_NN(MY_CXT.universal_isa); + + MY_CXT.universal_can = gv_fetchpvs("UNIVERSAL::can", GV_ADD, SVt_PVCV); + SvREFCNT_inc_simple_void_NN(MY_CXT.universal_can); } #define DEFINE_TC(name) mouse_tc_generate(aTHX_ "Mouse::Util::TypeConstraints::" STRINGIFY(name), CAT2(mouse_tc_, name), NULL) @@ -581,13 +665,11 @@ void compile_type_constraint(SV* self) CODE: { - AV* const checks = newAV(); + AV* const checks = newAV_mortal(); SV* check; /* check function */ SV* parent; SV* types_ref; - sv_2mortal((SV*)checks); - for(parent = get_slots(self, "parent"); parent; parent = get_slots(parent, "parent")){ check = get_slots(parent, "hand_optimized_type_constraint"); if(check && SvOK(check)){ @@ -631,8 +713,7 @@ CODE: types = (AV*)SvRV(types_ref); len = av_len(types) + 1; - union_checks = newAV(); - sv_2mortal((SV*)union_checks); + union_checks = newAV_mortal(); for(i = 0; i < len; i++){ SV* const tc = *av_fetch(types, i, TRUE);