int
mouse_tc_check(pTHX_ SV* const tc_code, SV* const sv) {
CV* const cv = (CV*)SvRV(tc_code);
- assert(SvTYPE(cv) == Svt_PVCV);
+ assert(SvTYPE(cv) == SVt_PVCV);
- if(CvISXSUB(cv)){ /* can be built-in tc */
- if(CvXSUB(cv) == XS_Mouse__Util__TypeConstraints_Item){
- assert(CvXSUBANY(cv).any_iv > 0);
-
- return mouse_builtin_tc_check(aTHX_ CvXSUBANY(cv).any_iv, sv);
- }
- else if(CvXSUB(cv) == XS_Mouse_parameterized_check){
- MAGIC* const mg = (MAGIC*)CvXSUBANY(cv).any_ptr;
+ if(CvXSUB(cv) == XS_Mouse__Util__TypeConstraints_Item){ /* built-in */
+ return mouse_builtin_tc_check(aTHX_ CvXSUBANY(cv).any_iv, sv);
+ }
+ else if(CvXSUB(cv) == XS_Mouse_parameterized_check){ /* complex type constraints */
+ MAGIC* const mg = (MAGIC*)CvXSUBANY(cv).any_ptr;
- assert(CvXSUBANY(cv).any_ptr != NULL);
+ assert(CvXSUBANY(cv).any_ptr != NULL);
- /* call the check function directly, skipping call_sv() */
- return CALL_FPTR((check_fptr_t)mg->mg_ptr)(aTHX_ mg->mg_obj, sv);
- }
+ /* call the check function directly, skipping call_sv() */
+ return CALL_FPTR((check_fptr_t)mg->mg_ptr)(aTHX_ mg->mg_obj, sv);
}
-
- /* user-defined type constraints */
- {
+ else { /* custom */
int ok;
dSP;
switch(tc){
case MOUSE_TC_ANY: return mouse_tc_Any(aTHX_ sv);
case MOUSE_TC_ITEM: return mouse_tc_Any(aTHX_ sv);
+ case MOUSE_TC_MAYBE: return mouse_tc_Any(aTHX_ sv);
case MOUSE_TC_UNDEF: return mouse_tc_Undef(aTHX_ sv);
case MOUSE_TC_DEFINED: return mouse_tc_Defined(aTHX_ sv);
case MOUSE_TC_BOOL: return mouse_tc_Bool(aTHX_ sv);
return SvROK(sv) && SvOBJECT(SvRV(sv)) && !SvRXOK(sv);
}
+/* Parameterized type constraints */
+
+static int
+mouse_parameterized_ArrayRef(pTHX_ SV* const param, SV* const sv) {
+ if(mouse_tc_ArrayRef(aTHX_ sv)){
+ AV* const av = (AV*)SvRV(sv);
+ I32 const len = av_len(av) + 1;
+ I32 i;
+ for(i = 0; i < len; i++){
+ SV* const value = *av_fetch(av, i, TRUE);
+ SvGETMAGIC(value);
+ if(!mouse_tc_check(aTHX_ param, value)){
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+mouse_parameterized_HashRef(pTHX_ SV* const param, SV* const sv) {
+ if(mouse_tc_HashRef(aTHX_ sv)){
+ HV* const hv = (HV*)SvRV(sv);
+ HE* he;
+
+ hv_iterinit(hv);
+ while((he = hv_iternext(hv))){
+ SV* const value = hv_iterval(hv, he);
+ SvGETMAGIC(value);
+ if(!mouse_tc_check(aTHX_ param, value)){
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+mouse_parameterized_Maybe(pTHX_ SV* const param, SV* const sv) {
+ if(SvOK(sv)){
+ return mouse_tc_check(aTHX_ param, sv);
+ }
+ return TRUE;
+}
+
+static int
+mouse_types_union_check(pTHX_ AV* const types, SV* const sv) {
+ I32 const len = AvFILLp(types) + 1;
+ I32 i;
+
+ for(i = 0; i < len; i++){
+ if(mouse_tc_check(aTHX_ AvARRAY(types)[i], sv)){
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static int
+mouse_types_check(pTHX_ AV* const types, SV* const sv) {
+ I32 const len = AvFILLp(types) + 1;
+ I32 i;
+
+ ENTER;
+ SAVE_DEFSV;
+ DEFSV_set(sv);
+
+ for(i = 0; i < len; i++){
+ if(!mouse_tc_check(aTHX_ AvARRAY(types)[i], sv)){
+ LEAVE;
+ return FALSE;
+ }
+ }
+
+ LEAVE;
+
+ return TRUE;
+}
+
/*
* This class_type generator is taken from Scalar::Util::Instance
*/
static MGVTBL mouse_util_type_constraints_vtbl; /* not used, only for identity */
+static CV*
+mouse_tc_parameterize(pTHX_ const char* const name, check_fptr_t const fptr, SV* const param) {
+ CV* xsub;
+
+ xsub = newXS(name, XS_Mouse_parameterized_check, __FILE__);
+ CvXSUBANY(xsub).any_ptr = sv_magicext(
+ (SV*)xsub,
+ param, /* mg_obj: refcnt will be increased */
+ PERL_MAGIC_ext,
+ &mouse_util_type_constraints_vtbl,
+ (void*)fptr, /* mg_ptr */
+ 0 /* mg_len: 0 for static data */
+ );
+
+ if(!name){
+ sv_2mortal((SV*)xsub);
+ }
+
+ return xsub;
+}
+
CV*
mouse_generate_isa_predicate_for(pTHX_ SV* const klass, const char* const predicate_name){
STRLEN klass_len;
const char* klass_pv = SvPV_const(klass, klass_len);
- CV* xsub;
- SV* mg_obj;
- void* mg_ptr;
+ SV* param;
+ void* fptr;
klass_pv = mouse_canonicalize_package_name(klass_pv);
if(strNE(klass_pv, "UNIVERSAL")){
- mg_obj = (SV*)gv_stashpvn(klass_pv, klass_len, GV_ADD);
- mg_ptr = (void*)mouse_is_an_instance_of;
+ param = (SV*)gv_stashpvn(klass_pv, klass_len, GV_ADD);
+ fptr = (void*)mouse_is_an_instance_of;
}
else{
- mg_obj = NULL;
- mg_ptr = (void*)mouse_is_an_instance_of_universal;
- }
-
- xsub = newXS(predicate_name, XS_Mouse_parameterized_check, __FILE__);
-
- CvXSUBANY(xsub).any_ptr = sv_magicext(
- (SV*)xsub,
- mg_obj, /* refcnt will be increased */
- PERL_MAGIC_ext,
- &mouse_util_type_constraints_vtbl,
- mg_ptr,
- 0 /* indicates static data */
- );
-
- if(!predicate_name){
- sv_2mortal((SV*)xsub);
+ param = NULL;
+ fptr = (void*)mouse_is_an_instance_of_universal;
}
- return xsub;
+ return mouse_tc_parameterize(aTHX_ predicate_name, fptr, param);
}
XS(XS_Mouse_parameterized_check) {
XSRETURN(1);
+CV*
+_parameterize_ArrayRef_for(SV* param)
+ALIAS:
+ _parameterize_ArrayRef_for = MOUSE_TC_ARRAY_REF
+ _parameterize_HashRef_for = MOUSE_TC_HASH_REF
+ _parameterize_Maybe_for = MOUSE_TC_MAYBE
+CODE:
+{
+ check_fptr_t fptr;
+ SV* const tc_code = mcall0s(param, "_compiled_type_constraint");
+ if(!(SvROK(tc_code) && SvTYPE(SvRV(tc_code)) == SVt_PVCV)){
+ croak("_compiled_type_constraint didn't return a CODE reference");
+ }
+
+ switch(ix){
+ case MOUSE_TC_ARRAY_REF:
+ fptr = mouse_parameterized_ArrayRef;
+ break;
+ case MOUSE_TC_HASH_REF:
+ fptr = mouse_parameterized_HashRef;
+ break;
+ default: /* Maybe type */
+ fptr = mouse_parameterized_Maybe;
+ }
+ RETVAL = mouse_tc_parameterize(aTHX_ NULL, fptr, tc_code);
+}
+OUTPUT:
+ RETVAL
+
+MODULE = Mouse::Util::TypeConstraints PACKAGE = Mouse::Meta::TypeConstraint
+
+void
+compile_type_constraint(SV* self)
+CODE:
+{
+ AV* const checks = newAV();
+ 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)){
+ if(!mouse_tc_CodeRef(aTHX_ check)){
+ croak("Not a CODE reference");
+ }
+ av_unshift(checks, 1);
+ av_store(checks, 0, newSVsv(check));
+ break; /* a hand optimized constraint must include all the parent */
+ }
+
+ check = get_slots(parent, "constraint");
+ if(check && SvOK(check)){
+ if(!mouse_tc_CodeRef(aTHX_ check)){
+ croak("Not a CODE reference");
+ }
+ av_unshift(checks, 1);
+ av_store(checks, 0, newSVsv(check));
+ }
+ }
+
+ check = get_slots(self, "constraint");
+ if(check && SvOK(check)){
+ if(!mouse_tc_CodeRef(aTHX_ check)){
+ croak("Not a CODE reference");
+ }
+ av_push(checks, newSVsv(check));
+ }
+
+ types_ref = get_slots(self, "type_constraints");
+ if(types_ref && SvOK(types_ref)){ /* union type */
+ AV* types;
+ AV* union_checks;
+ CV* union_check;
+ I32 len;
+ I32 i;
+
+ if(!mouse_tc_ArrayRef(aTHX_ types_ref)){
+ croak("Not an ARRAY reference");
+ }
+ types = (AV*)SvRV(types_ref);
+ len = av_len(types) + 1;
+
+ union_checks = newAV();
+ sv_2mortal((SV*)union_checks);
+
+ for(i = 0; i < len; i++){
+ SV* const tc = *av_fetch(types, i, TRUE);
+ SV* const c = get_slots(tc, "compiled_type_constraint");
+ if(!(c && mouse_tc_CodeRef(aTHX_ c))){
+ sv_dump(self);
+ croak("'%"SVf"' has no compiled type constraint", self);
+ }
+ av_push(union_checks, newSVsv(c));
+ }
+
+ union_check = mouse_tc_parameterize(aTHX_ NULL, (check_fptr_t)mouse_types_union_check, (SV*)union_checks);
+ av_push(checks, newRV_inc((SV*)union_check));
+ }
+
+ if(AvFILLp(checks) < 0){
+ check = newRV_inc((SV*)get_cv("Mouse::Util::TypeConstraints::Any", TRUE));
+ }
+ else{
+ check = newRV_inc((SV*)mouse_tc_parameterize(aTHX_ NULL, (check_fptr_t)mouse_types_check, (SV*)checks));
+ }
+ set_slots(self, "compiled_type_constraint", check);
+}
+