Add _initialize_object()
gfx [Sun, 15 Nov 2009 08:47:25 +0000 (17:47 +0900)]
lib/Mouse/Meta/Class.pm
lib/Mouse/PurePerl.pm
mouse.h
xs-src/Mouse.xs
xs-src/MouseAccessor.xs
xs-src/MouseAttribute.xs

index 75fd5af..8183a96 100644 (file)
@@ -11,7 +11,7 @@ our @ISA = qw(Mouse::Meta::Module);
 sub method_metaclass()    { 'Mouse::Meta::Method'    }
 sub attribute_metaclass() { 'Mouse::Meta::Attribute' }
 
-sub constructor_class()   { 'Mouse::Meta::Method::Constructor' }
+sub constructor_class(); # XS
 sub destructor_class()    { 'Mouse::Meta::Method::Destructor'  }
 
 sub _construct_meta {
@@ -166,62 +166,6 @@ sub new_object {
     return $object;
 }
 
-sub _initialize_object{
-    my($self, $object, $args, $ignore_triggers) = @_;
-
-    my @triggers_queue;
-
-    foreach my $attribute ($self->get_all_attributes) {
-        my $from = $attribute->init_arg;
-        my $key  = $attribute->name;
-
-        if (defined($from) && exists($args->{$from})) {
-            $object->{$key} = $attribute->_coerce_and_verify($args->{$from}, $object);
-
-            weaken($object->{$key})
-                if ref($object->{$key}) && $attribute->is_weak_ref;
-
-            if ($attribute->has_trigger) {
-                push @triggers_queue, [ $attribute->trigger, $object->{$key} ];
-            }
-        }
-        else {
-            if ($attribute->has_default || $attribute->has_builder) {
-                unless ($attribute->is_lazy) {
-                    my $default = $attribute->default;
-                    my $builder = $attribute->builder;
-                    my $value =   $builder                ? $object->$builder()
-                                : ref($default) eq 'CODE' ? $object->$default()
-                                :                           $default;
-
-                    $object->{$key} = $attribute->_coerce_and_verify($value, $object);
-
-                    weaken($object->{$key})
-                        if ref($object->{$key}) && $attribute->is_weak_ref;
-                }
-            }
-            else {
-                if ($attribute->is_required) {
-                    $self->throw_error("Attribute (".$attribute->name.") is required");
-                }
-            }
-        }
-    }
-
-    if(!$ignore_triggers){
-        foreach my $trigger_and_value(@triggers_queue){
-            my($trigger, $value) = @{$trigger_and_value};
-            $trigger->($object, $value);
-        }
-    }
-
-    if($self->is_anon_class){
-        $object->{__METACLASS__} = $self;
-    }
-
-    return $object;
-}
-
 sub clone_object {
     my $class  = shift;
     my $object = shift;
index a6f1be4..e50b57a 100644 (file)
@@ -203,6 +203,8 @@ sub add_method {
 package
     Mouse::Meta::Class;
 
+sub constructor_class()   { 'Mouse::Meta::Method::Constructor' }
+
 sub is_anon_class{
     return exists $_[0]->{anon_serial_id};
 }
@@ -217,6 +219,61 @@ sub get_all_attributes {
     return values %attrs;
 }
 
+sub _initialize_object{
+    my($self, $object, $args, $ignore_triggers) = @_;
+
+    my @triggers_queue;
+
+    foreach my $attribute ($self->get_all_attributes) {
+        my $init_arg = $attribute->init_arg;
+        my $slot     = $attribute->name;
+
+        if (defined($init_arg) && exists($args->{$init_arg})) {
+            $object->{$slot} = $attribute->_coerce_and_verify($args->{$init_arg}, $object);
+
+            weaken($object->{$slot})
+                if ref($object->{$slot}) && $attribute->is_weak_ref;
+
+            if ($attribute->has_trigger) {
+                push @triggers_queue, [ $attribute->trigger, $object->{$slot} ];
+            }
+        }
+        else { # no init arg
+            if ($attribute->has_default || $attribute->has_builder) {
+                if (!$attribute->is_lazy) {
+                    my $default = $attribute->default;
+                    my $builder = $attribute->builder;
+                    my $value =   $builder                ? $object->$builder()
+                                : ref($default) eq 'CODE' ? $object->$default()
+                                :                           $default;
+
+                    $object->{$slot} = $attribute->_coerce_and_verify($value, $object);
+
+                    weaken($object->{$slot})
+                        if ref($object->{$slot}) && $attribute->is_weak_ref;
+                }
+            }
+            elsif($attribute->is_required) {
+                $self->throw_error("Attribute (".$attribute->name.") is required");
+            }
+        }
+    }
+
+    if(!$ignore_triggers){
+        foreach my $trigger_and_value(@triggers_queue){
+            my($trigger, $value) = @{$trigger_and_value};
+            $trigger->($object, $value);
+        }
+    }
+
+    if($self->is_anon_class){
+        $object->{__METACLASS__} = $self;
+    }
+
+    return;
+}
+
+
 package
     Mouse::Meta::Role;
 
diff --git a/mouse.h b/mouse.h
index a359b14..0be0825 100644 (file)
--- a/mouse.h
+++ b/mouse.h
@@ -190,11 +190,14 @@ XS(XS_Mouse_constraint_check);
 /* Mouse XS Attribute object */
 
 AV* mouse_get_xa(pTHX_ SV* const attr);
+SV* mouse_xa_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags);
+SV* mouse_xa_set_default(pTHX_ AV* const xa, SV* const object);
 
 enum mouse_xa_ix_t{
     MOUSE_XA_SLOT,      /* for constructors, sync to mg_obj */
     MOUSE_XA_FLAGS,     /* for constructors, sync to mg_private */
     MOUSE_XA_ATTRIBUTE,
+    MOUSE_XA_INIT_ARG,
     MOUSE_XA_TC,
     MOUSE_XA_TC_CODE,
 
@@ -202,17 +205,17 @@ enum mouse_xa_ix_t{
 };
 
 #define MOUSE_xa_slot(m)      MOUSE_av_at(m, MOUSE_XA_SLOT)
-#define MOUSE_xa_flags(m)     MOUSE_av_at(m, MOUSE_XA_FLAGS)
+#define MOUSE_xa_flags(m)     SvUVX( MOUSE_av_at(m, MOUSE_XA_FLAGS) )
 #define MOUSE_xa_attribute(m) MOUSE_av_at(m, MOUSE_XA_ATTRIBUTE)
+#define MOUSE_xa_init_arg(m)  MOUSE_av_at(m, MOUSE_XA_INIT_ARG)
 #define MOUSE_xa_tc(m)        MOUSE_av_at(m, MOUSE_XA_TC)
 #define MOUSE_xa_tc_code(m)   MOUSE_av_at(m, MOUSE_XA_TC_CODE)
 
-
 enum mouse_xa_flags_t{
     MOUSEf_ATTR_HAS_TC          = 0x0001,
     MOUSEf_ATTR_HAS_DEFAULT     = 0x0002,
     MOUSEf_ATTR_HAS_BUILDER     = 0x0004,
-    MOUSEf_ATTR_HAS_INITIALIZER = 0x0008,
+    MOUSEf_ATTR_HAS_INITIALIZER = 0x0008, /* not used */
     MOUSEf_ATTR_HAS_TRIGGER     = 0x0010,
 
     MOUSEf_ATTR_IS_LAZY         = 0x0020,
index bec3c7d..e518b03 100644 (file)
@@ -8,7 +8,7 @@ SV* mouse_name;
 SV* mouse_get_attribute;
 SV* mouse_get_attribute_list;
 
-
+#define MOUSE_xc_flags(a)       SvUVX(MOUSE_av_at((a), MOUSE_XC_FLAGS))
 #define MOUSE_xc_gen(a)         MOUSE_av_at((a), MOUSE_XC_GEN)
 #define MOUSE_xc_stash(a)       ( (HV*)MOUSE_av_at((a), MOUSE_XC_STASH) )
 #define MOUSE_xc_attrall(a)     ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) )
@@ -25,9 +25,10 @@ enum mouse_xc_flags_t {
 
 /* Mouse XS Metaclass object */
 enum mouse_xc_ix_t{
+    MOUSE_XC_FLAGS,
+
     MOUSE_XC_GEN,          /* class generation */
     MOUSE_XC_STASH,        /* symbol table hash */
-    MOUSE_XC_FLAGS,
 
     MOUSE_XC_BUILDARGS,    /* Custom BUILDARGS */
 
@@ -102,14 +103,19 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas
 
     /* update */
 
-    if(SvTRUEx( mcall0s(metaclass, "is_immutable") )){
+    if(predicate_calls(metaclass, "is_immutable")){
         flags |= MOUSEf_XC_IS_IMMUTABLE;
     }
 
+    if(predicate_calls(metaclass, "is_anon_class")){
+        flags |= MOUSEf_XC_IS_ANON;
+    }
+
     if(mouse_class_has_custom_buildargs(aTHX_ stash)){
         flags |= MOUSEf_XC_HAS_BUILDARGS;
     }
 
+    av_store(xc, MOUSE_XC_FLAGS,       newSVuv(flags));
     av_store(xc, MOUSE_XC_ATTRALL,     (SV*)attrall);
     av_store(xc, MOUSE_XC_BUILDALL,    (SV*)buildall);
     av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
@@ -171,6 +177,7 @@ mouse_get_xc(pTHX_ SV* const metaclass) {
 
         av_store(xc, MOUSE_XC_GEN, newSViv(0));
         av_store(xc, MOUSE_XC_STASH, (SV*)stash);
+
         SvREFCNT_inc_simple_void_NN(stash);
     }
     else{
@@ -224,13 +231,72 @@ void
 mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) {
     AV* const xc    = mouse_get_xc(aTHX_ meta);
     AV* const attrs = MOUSE_xc_attrall(xc);
-    I32 const len   = AvFILLp(attrs) + 1;
+    I32 len         = AvFILLp(attrs) + 1;
     I32 i;
-    AV* const triggers_queue = (ignore_triggers ? NULL : newAV_mortal());
+    AV* triggers_queue = NULL;
+
+    ENTER;
+    SAVETMPS;
+
+    if(!ignore_triggers){
+        triggers_queue = newAV_mortal();
+    }
 
     for(i = 0; i < len; i++){
-        AV* const xa = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]);
+        SV* const attr = AvARRAY(attrs)[i];
+        AV* const xa   = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]);
+
+        SV* const slot     = MOUSE_xa_slot(xa);
+        U16 const flags    = (U16)MOUSE_xa_flags(xa);
+        SV* const init_arg = MOUSE_xa_init_arg(xa);
+        HE* he;
+
+        if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){
+            SV* value = HeVAL(he);
+            if(flags & MOUSEf_ATTR_HAS_TC){
+                value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
+            }
+            set_slot(object, slot, value);
+            if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){
+                weaken_slot(object, slot);
+            }
+            if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){
+                AV* const pair = newAV();
+                av_push(pair, newSVsv( mcall0s(attr, "trigger") ));
+                av_push(pair, newSVsv(value));
+
+                av_push(triggers_queue, (SV*)pair);
+            }
+        }
+        else { /* no init arg */
+            if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){
+                if(!(flags & MOUSEf_ATTR_IS_LAZY)){
+                    mouse_xa_set_default(aTHX_ xa, object);
+                }
+            }
+            else if(flags & MOUSEf_ATTR_IS_REQUIRED) {
+                mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot);
+            }
+        }
+    } /* for each attributes */
+
+    if(triggers_queue){
+        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];
+            SV* const value   = AvARRAY(pair)[1];
+
+            mcall1(object, trigger, value);
+        }
+    }
+
+    if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
+        set_slot(object, newSVpvs_flags("__ANON__", SVs_TEMP), meta);
     }
+
+    FREETMPS;
+    LEAVE;
 }
 
 MODULE = Mouse  PACKAGE = Mouse
@@ -332,6 +398,8 @@ MODULE = Mouse  PACKAGE = Mouse::Meta::Class
 BOOT:
     INSTALL_SIMPLE_READER(Class, roles);
     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
+    newCONSTSUB(gv_stashpvs("Mouse::Meta::Class", TRUE), "constructor_class",
+        newSVpvs("Mouse::Meta::Method::Constructor::XS"));
 
 void
 linearized_isa(SV* self)
@@ -380,7 +448,7 @@ CODE:
 
 
 void
-_initialize_object_(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE)
+_initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE)
 CODE:
 {
     mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers);
@@ -403,3 +471,15 @@ CODE:
 OUTPUT:
     RETVAL
 
+MODULE = Mouse  PACKAGE = Mouse::Meta::Method::Constructor::XS
+
+CV*
+_generate_constructor(...)
+CODE:
+{
+    RETVAL = get_cvs("Mouse::Object::new", TRUE);
+    SvREFCNT_inc_simple_void_NN(RETVAL);
+}
+OUTPUT:
+    RETVAL
+
index 86b0b9f..ef5009a 100644 (file)
@@ -37,7 +37,7 @@ mouse_instantiate_xs_accessor(pTHX_ SV* const attr, XSUBADDR_t const accessor_im
 
     mg = sv_magicext((SV*)xsub, MOUSE_xa_slot(xa), PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)xa, HEf_SVKEY);
 
-    MOUSE_mg_flags(mg) = (U16)SvUV(MOUSE_xa_flags(xa));
+    MOUSE_mg_flags(mg) = (U16)MOUSE_xa_flags(xa);
 
     /* NOTE:
      * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx)
@@ -47,36 +47,6 @@ mouse_instantiate_xs_accessor(pTHX_ SV* const attr, XSUBADDR_t const accessor_im
     return xsub;
 }
 
-static SV*
-mouse_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags){
-    SV* const tc = MOUSE_xa_tc(xa);
-    SV* tc_code;
-
-    if(flags & MOUSEf_ATTR_SHOULD_COERCE){
-          value = mcall1s(tc, "coerce", value);
-    }
-
-    if(!SvOK(MOUSE_xa_tc_code(xa))){
-        tc_code = mcall0s(tc, "_compiled_type_constraint");
-        av_store(xa, MOUSE_XA_TC_CODE, newSVsv(tc_code));
-
-        if(!IsCodeRef(tc_code)){
-            mouse_throw_error(MOUSE_xa_attribute(xa), tc, "Not a CODE reference");
-        }
-    }
-    else{
-        tc_code = MOUSE_xa_tc_code(xa);
-    }
-
-    if(!mouse_tc_check(aTHX_ tc_code, value)){
-        mouse_throw_error(MOUSE_xa_attribute(xa), value,
-            "Attribute (%"SVf") does not pass the type constraint because: %"SVf,
-                mcall0(MOUSE_xa_attribute(xa), mouse_name),
-                mcall1s(tc, "get_message", value));
-    }
-
-    return value;
-}
 
 #define PUSH_VALUE(value, flags) STMT_START { \
         if((flags) & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY){ \
@@ -140,36 +110,13 @@ mouse_push_values(pTHX_ SV* const value, U16 const flags){
 static void
 mouse_attr_get(pTHX_ SV* const self, MAGIC* const mg){
     U16 const flags = MOUSE_mg_flags(mg);
-    SV* const slot  = MOUSE_mg_slot(mg);
     SV* value;
 
-    value = get_slot(self, slot);
+    value = get_slot(self, MOUSE_mg_slot(mg));
 
     /* check_lazy */
     if( !value && flags & MOUSEf_ATTR_IS_LAZY ){
-        AV* const xa   = MOUSE_mg_xa(mg);
-        SV* const attr = MOUSE_xa_attribute(xa);
-
-        /* get default value by $attr->builder or $attr->default */
-        if(flags & MOUSEf_ATTR_HAS_BUILDER){
-            SV* const builder = mcall0s(attr, "builder");
-            value = mcall0(self, builder);
-        }
-        else {
-            value = mcall0s(attr, "default");
-
-            if(IsCodeRef(value)){
-                value = mcall0(self, value);
-            }
-        }
-
-        /* apply coerce and type constraint */
-        if(flags & MOUSEf_ATTR_HAS_TC){
-            value = mouse_apply_type_constraint(aTHX_ xa, value, flags);
-        }
-
-        /* store value to slot */
-        value = set_slot(self, slot, value);
+        value = mouse_xa_set_default(aTHX_ MOUSE_mg_xa(mg), self);
     }
 
     PUSH_VALUE(value, flags);
@@ -181,7 +128,7 @@ mouse_attr_set(pTHX_ SV* const self, MAGIC* const mg, SV* value){
     SV* const slot  = MOUSE_mg_slot(mg);
 
     if(flags & MOUSEf_ATTR_HAS_TC){
-        value = mouse_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
+        value = mouse_xa_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
     }
 
     set_slot(self, slot, value);
index 9bda4c6..62b2593 100644 (file)
@@ -35,6 +35,8 @@ mouse_get_xa(pTHX_ SV* const attr) {
 
         av_store(xa, MOUSE_XA_ATTRIBUTE, newSVsv(attr));
 
+        av_store(xa, MOUSE_XA_INIT_ARG, newSVsv(mcall0s(attr, "init_arg")));
+
         if(predicate_calls(attr, "has_type_constraint")){
             SV* tc;
             flags |= MOUSEf_ATTR_HAS_TC;
@@ -71,13 +73,12 @@ mouse_get_xa(pTHX_ SV* const attr) {
 
         if(predicate_calls(attr, "is_lazy")){
             flags |= MOUSEf_ATTR_IS_LAZY;
-
-            if(predicate_calls(attr, "has_builder")){
-                flags |= MOUSEf_ATTR_HAS_BUILDER;
-            }
-            else if(predicate_calls(attr, "has_default")){
-                flags |= MOUSEf_ATTR_HAS_DEFAULT;
-            }
+        }
+        if(predicate_calls(attr, "has_builder")){
+            flags |= MOUSEf_ATTR_HAS_BUILDER;
+        }
+        else if(predicate_calls(attr, "has_default")){
+            flags |= MOUSEf_ATTR_HAS_DEFAULT;
         }
 
         if(predicate_calls(attr, "is_weak_ref")){
@@ -104,6 +105,70 @@ mouse_get_xa(pTHX_ SV* const attr) {
     return xa;
 }
 
+SV*
+mouse_xa_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags){
+    SV* const tc = MOUSE_xa_tc(xa);
+    SV* tc_code;
+
+    if(flags & MOUSEf_ATTR_SHOULD_COERCE){
+          value = mcall1s(tc, "coerce", value);
+    }
+
+    if(!SvOK(MOUSE_xa_tc_code(xa))){
+        tc_code = mcall0s(tc, "_compiled_type_constraint");
+        av_store(xa, MOUSE_XA_TC_CODE, newSVsv(tc_code));
+
+        if(!IsCodeRef(tc_code)){
+            mouse_throw_error(MOUSE_xa_attribute(xa), tc, "Not a CODE reference");
+        }
+    }
+    else{
+        tc_code = MOUSE_xa_tc_code(xa);
+    }
+
+    if(!mouse_tc_check(aTHX_ tc_code, value)){
+        mouse_throw_error(MOUSE_xa_attribute(xa), value,
+            "Attribute (%"SVf") does not pass the type constraint because: %"SVf,
+                mcall0(MOUSE_xa_attribute(xa), mouse_name),
+                mcall1s(tc, "get_message", value));
+    }
+
+    return value;
+}
+
+
+SV*
+mouse_xa_set_default(pTHX_ AV* const xa, SV* const object) {
+    U16 const flags = (U16)MOUSE_xa_flags(xa);
+    SV* value;
+
+    /* get default value by $attr->builder or $attr->default */
+    if(flags & MOUSEf_ATTR_HAS_BUILDER){
+        SV* const builder = mcall0s(MOUSE_xa_attribute(xa), "builder");
+        value = mcall0(object, builder); /* $object->$builder() */
+    }
+    else {
+        value = mcall0s(MOUSE_xa_attribute(xa), "default");
+
+        if(IsCodeRef(value)){
+            value = mcall0(object, value);
+        }
+    }
+
+    /* apply coerce and type constraint */
+    if(flags & MOUSEf_ATTR_HAS_TC){
+        value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
+    }
+
+    /* store value to slot */
+    value = set_slot(object, MOUSE_xa_slot(xa), value);
+
+    if(flags & MOUSEf_ATTR_IS_WEAK_REF && SvROK(value)){
+        weaken_slot(object, MOUSE_xa_slot(xa));
+    }
+
+    return value;
+}
 
 MODULE = Mouse::Meta::Attribute  PACKAGE = Mouse::Meta::Attribute