}
}
else{
- /* false must be boolean */
+ /* any false value must be boolean */
return TRUE;
}
}
SV* const value = hv_iterval(hv, he);
SvGETMAGIC(value);
if(!mouse_tc_check(aTHX_ param, value)){
+ hv_iterinit(hv); /* reset */
return FALSE;
}
}
#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
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);
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)){
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*
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;
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)
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)){
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);