basic type checking, weaken
Yuval Kogman [Tue, 19 Aug 2008 21:54:20 +0000 (21:54 +0000)]
Moose.xs
t/700_xs/001_basic.t

index 50696e2..ea5f161 100644 (file)
--- a/Moose.xs
+++ b/Moose.xs
@@ -104,28 +104,26 @@ typedef enum {
     FileHandle,
     RegexpRef,
     Object,
+    ClassName,
     /* complex checks */
     Role,
-    ClassName,
     Enum,
 } TC;
 
 typedef enum {
     tc_none = 0,
     tc_type,
+    tc_stash,
     tc_cv,
     tc_op,
-    tc_stash,
-    tc_classname,
     tc_fptr,
 } tc_kind;
 
 typedef union {
     TC type;
+    HV *stash;
     CV *cv;
     OP *op;
-    HV *stash;
-    char *classname;
     bool (*fptr)(pTHX_ SV *type_constraint, SV *sv);
 } TC_CHECK;
 
@@ -281,7 +279,7 @@ STATIC bool check_reftype(TC type, SV *sv) {
             break;
     }
 
-    return SvTYPE(sv) == svt;
+    return SvTYPE(SvRV(sv)) == svt;
 }
 
 STATIC bool check_sv_class (pTHX_ HV *stash, SV *sv) {
@@ -320,6 +318,7 @@ STATIC bool check_sv_class (pTHX_ HV *stash, SV *sv) {
 STATIC bool check_sv_type (TC type, SV *sv) {
     if (!sv)
         return 0;
+
     switch (type) {
         case Any:
             return 1;
@@ -332,6 +331,30 @@ STATIC bool check_sv_type (TC type, SV *sv) {
             break;
         case Str:
             return (SvOK(sv) && !SvROK(sv));
+        case Num:
+#if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION <5)
+            if (!SvPOK(sv) && !SvPOKp(sv))
+                return SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK);
+            else
+#endif
+                return looks_like_number(sv);
+            break;
+        case Int:
+            if ( SvIOK(sv) ) {
+                return 1;
+            } else if ( SvPOK(sv) ) {
+                croak("todo");
+                int i;
+                STRLEN len;
+                char *pv = SvPV(sv, len);
+                char *end = pv + len;
+
+                errno = 0;
+                i = strtol(pv, &end, 0);
+                return !errno;
+            }
+            return 0;
+            break;
         case Ref:
             return SvROK(sv);
             break;
@@ -347,6 +370,14 @@ STATIC bool check_sv_type (TC type, SV *sv) {
         case Object:
             return sv_isobject(sv);
             break;
+        case ClassName:
+            {
+                STRLEN len;
+                char *pv;
+                pv = SvPV(sv, len);
+                return ( gv_stashpvn(pv, len, 0) != NULL );
+                break;
+            }
         case RegexpRef:
             return sv_isa(sv, "Regexp");
             break;
@@ -360,7 +391,7 @@ STATIC bool check_sv_type (TC type, SV *sv) {
     return 0;
 }
 
-STATIC bool check_sv_type_constraint(pTHX_ tc_kind kind, TC_CHECK tc_check, SV *type_constraint, SV *sv) {
+STATIC bool check_type_constraint(pTHX_ tc_kind kind, TC_CHECK tc_check, SV *type_constraint, SV *sv) {
     switch (kind) {
         case tc_none:
             return 1;
@@ -371,9 +402,6 @@ STATIC bool check_sv_type_constraint(pTHX_ tc_kind kind, TC_CHECK tc_check, SV *
         case tc_stash:
             return check_class(aTHX_ tc_check.stash, sv);
             break;
-        case tc_classname:
-            return ( gv_stashpv(tc_check.classname, 0) != NULL );
-            break;
         case tc_fptr:
             return tc_check.fptr(aTHX_ type_constraint, sv);
             break;
@@ -388,41 +416,95 @@ STATIC bool check_sv_type_constraint(pTHX_ tc_kind kind, TC_CHECK tc_check, SV *
 }
 
 
-STATIC void init_attr (MI *mi, ATTR *attr, HV *desc) {
+STATIC void init_attr (MI *mi, ATTR *attr, AV *desc) {
+    U32 flags = 0;
     U32 hash;
     STRLEN len;
-    SV **key = hv_fetchs(desc, "key", 0);
-    SV **meta_attr = hv_fetchs(desc, "meta", 0);
     char *pv;
-
-    if ( !meta_attr ) croak("'meta' is required");
-
-    attr->meta_attr = newSVsv(*meta_attr);
+    I32 ix = av_len(desc);
+    SV **params = AvARRAY(desc);
+    SV *tc;
+    SV *key;
 
     attr->mi = mi;
 
-    attr->flags = 0;
 
+    if ( ix != 12 )
+        croak("wrong number of args (%d != 13)", ix + 1);
 
-    /* if type == hash */
-    /* prehash the key */
-    if ( !key ) croak("'key' is required");
+    for ( ; ix >= 0; ix-- ) {
+        if ( !params[ix] || params[ix] == &PL_sv_undef )
+            croak("bad params");
+    }
+
+    if ( !SvROK(params[1]) || SvTYPE(SvRV(params[1])) != SVt_PVAV )
+        croak("slots is not an array");
 
-    pv = SvPV(*key, len);
+    if ( av_len((AV *)SvRV(params[1])) != 0 )
+        croak("Only unary slots are supported at the moment");
 
+    /* calculate a hash from the slot */
+    /* FIXME arrays etc should also be supported */
+    key = *av_fetch((AV *)SvRV(params[1]), 0, 0);
+    pv = SvPV(key, len);
     PERL_HASH(hash, pv, len);
 
-    attr->slot_sv = newSVpvn_share(pv, len, hash);
-    attr->slot_u32 = hash;
 
-    attr->def.type = 0;
+    /* FIXME better organize these */
+    if ( SvTRUE(params[2]) )
+        flags |= ATTR_WEAK;
+
+    if ( SvTRUE(params[3]) )
+        flags |= ATTR_COERCE;
 
-    attr->tc_check.type = 0;
-    attr->type_constraint = NULL;
+    if ( SvTRUE(params[4]) )
+        flags |= ATTR_LAZY;
 
+    tc = params[5];
 
-    attr->initializer = NULL;
-    attr->trigger = NULL;
+    if ( SvOK(tc) ) {
+        int tc_kind = SvIV(params[6]);
+        SV *data = params[7];
+
+        switch (tc_kind) {
+            case tc_stash:
+                attr->tc_check.stash = gv_stashsv(data, 0);
+                break;
+            case tc_type:
+                attr->tc_check.type = SvIV(data);
+                break;
+            case tc_cv:
+                attr->tc_check.cv = (CV *)SvRV(data);
+                if ( SvTYPE(attr->tc_check.cv) != SVt_PVCV )
+                    croak("compiled type constraint is not a coderef");
+                break;
+            default:
+                croak("todo");
+        }
+
+        flags |= tc_kind;
+    }
+
+    attr->flags = flags; /* FIXME default_kind */
+
+    attr->trigger = SvROK(params[6]) ? (CV *)SvRV(params[6]) : NULL;
+    if ( attr->trigger && SvTYPE(attr->trigger) != SVt_PVCV )
+        croak("trigger is not a coderef");
+
+    attr->initializer = SvROK(params[7]) ? (CV *)SvRV(params[7]) : NULL;
+    if ( attr->initializer && SvTYPE(attr->initializer) != SVt_PVCV )
+        croak("initializer is not a coderef");
+
+    /* copy refs */
+    attr->meta_attr       = newSVsv(params[0]);
+    attr->type_constraint = newSVsv(tc);
+    if ( attr->trigger )     SvREFCNT_inc(attr->trigger);
+    if ( attr->initializer ) SvREFCNT_inc(attr->initializer);
+
+    attr->slot_sv = newSVpvn_share(pv, len, hash);
+    attr->slot_u32 = hash;
+
+    attr->def.type = 0;
 
     /* cross refs to CVs which use this struct */
     attr->cvs = newAV();
@@ -446,11 +528,11 @@ STATIC MI *new_mi (pTHX_ HV *stash, AV *attrs) {
     for ( ix = 0; ix < num; ix++ ) {
         SV **desc = av_fetch(attrs, ix, 0);
 
-        if ( !desc || !*desc || !SvROK(*desc) || !(SvTYPE(SvRV(*desc)) == SVt_PVHV) ) {
+        if ( !desc || !*desc || !SvROK(*desc) || !(SvTYPE(SvRV(*desc)) == SVt_PVAV) ) {
             croak("Attribute descriptor has to be a hash reference");
         }
 
-        init_attr(mi, &mi->attrs[ix], (HV *)SvRV(*desc));
+        init_attr(mi, &mi->attrs[ix], (AV *)SvRV(*desc));
     }
 
     return mi;
@@ -639,6 +721,19 @@ STATIC SV *deinitialize_slot(pTHX_ SV *self, ATTR *attr) {
     return hv_delete_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32);
 }
 
+STATIC SV *getter_common(pTHX_ SV *self, ATTR *attr) {
+    assert( ATTR_DUMB_READER(attr) );
+    return get_slot_value(aTHX_ self, attr);
+}
+
+STATIC void setter_common(pTHX_ SV *self, ATTR *attr, SV *value) {
+    if ( attr->flags & ATTR_MASK_TYPE ) {
+        if ( !check_type_constraint(aTHX_ attr->flags & ATTR_MASK_TYPE, attr->tc_check, attr->type_constraint, value) )
+            croak("Bad param");
+    }
+
+    set_slot_value(aTHX_ self, attr, value);
+}
 
 /* simple high level api */
 
@@ -657,9 +752,7 @@ STATIC XS(getter)
 
     SP -= items;
 
-    assert( ATTR_DUMB_READER(attr) );
-
-    value = get_slot_value(aTHX_ ST(0), attr);
+    value = getter_common(aTHX_ ST(0), attr);
 
     if (value) {
         ST(0) = sv_mortalcopy(value); /* mortalcopy because $_ .= "blah" for $foo->bar */
@@ -683,9 +776,7 @@ STATIC XS(setter)
 
     SP -= items;
 
-    assert( ATTR_DUMB_WRITER(attr) );
-
-    set_slot_value(aTHX_ ST(0), attr, ST(1));
+    setter_common(aTHX_ ST(0), attr, ST(1));
 
     ST(0) = ST(1); /* return value */
     XSRETURN(1);
@@ -706,13 +797,10 @@ STATIC XS(accessor)
     SP -= items;
 
     if (items > 1) {
-        assert( ATTR_DUMB_READER(attr) );
-        set_slot_value(aTHX_ ST(0), attr, ST(1));
+        setter_common(aTHX_ ST(0), attr, ST(1));
         ST(0) = ST(1); /* return value */
     } else {
-        SV *value;
-        assert( ATTR_DUMB_WRITER(attr) );
-        value = get_slot_value(aTHX_ ST(0), attr);
+        SV *value = getter_common(aTHX_ ST(0), attr);
         if ( value ) {
             ST(0) = value;
         } else {
index 9ea2ef1..9e0048b 100644 (file)
@@ -26,15 +26,67 @@ BEGIN {
         return $attr->associated_class->get_meta_instance;
     }
 
+    my $i;
+    my %checks = map { $_ => $i++ } qw(
+        Any
+        Undef
+        Defined
+        Str
+        Num
+        Int
+        GlobRef
+        ArrayRef
+        HashRef
+        CodeRef
+        Ref
+        ScalarRef
+        FileHandle
+        RegexpRef
+        Object
+        ClassName
+    );
+
+    # aliases
+    $checks{Bool} = $checks{Item} = $checks{Any};
+    $checks{Value} = $checks{Str};
+
+    sub tc_params {
+        my $tc = shift;
+
+        return ( undef, 0, undef ) unless $tc;
+
+        if ( ref $tc eq 'Moose::Meta::TypeConstraint' or ref $tc eq 'Moose::Meta::TypeConstraint::Parameterizable') {
+            # builtin moose type #
+            return ( $tc, 1, $checks{$tc->name} );
+        } elsif ( $tc->isa("Moose::Meta::TypeConstraint::Class") ) {
+            return ( $tc, 2, $tc->class );
+        } else {
+            warn ref $tc;
+            return ( $tc, 3, $tc->_compiled_type_constraint );
+        }
+    }
+
     sub meta_instance_to_attr_descs {
         my $mi = shift;
 
         return (
             $mi->associated_metaclass->name,
-            [ map { {
-                meta => $_,
-                key  => ($_->slots)[0],
-            } } $mi->get_all_attributes ]
+            [ map {[
+                $_,
+                [$_->slots],
+
+                $_->is_weak_ref,
+                $_->should_coerce,
+                $_->is_lazy,
+
+                tc_params($_->type_constraint),
+                $_->trigger,
+                $_->initializer,
+
+                $_->has_default,
+                $_->default,
+                $_->builder,
+            ]} $mi->get_all_attributes ]
         );
     }
 }
@@ -55,6 +107,8 @@ ok( defined &Moose::XS::new_predicate );
     has i => ( isa => "Int", is => "rw" );
     has s => ( isa => "Str", is => "rw" );
     has a => ( isa => "ArrayRef", is => "rw" );
+
+    # FIXME Regexp, Class, ClassName, Object, parametrized, filehandle
 }
 
 {