closed
[gitmo/Class-MOP.git] / lib / Class / MOP / Attribute.pm
index 2eeee2d..5352826 100644 (file)
@@ -61,7 +61,7 @@ sub clone {
 }
 
 sub initialize_instance_slot {
-    my ($self, $instance, $params) = @_;
+    my ($self, $meta_instance, $instance, $params) = @_;
     my $init_arg = $self->{init_arg};
     # try to fetch the init arg from the %params ...
     my $val;        
@@ -71,9 +71,7 @@ sub initialize_instance_slot {
     if (!defined $val && defined $self->{default}) {
         $val = $self->default($instance);
     }
-
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    $meta_instance->set_slot_value_with_init( $instance, $self->slot_name, $val );
+    $meta_instance->set_slot_value($instance, $self->name, $val);
 }
 
 # NOTE:
@@ -100,18 +98,26 @@ sub init_arg  { $_[0]->{init_arg}  }
 # end bootstrapped away method section.
 # (all methods below here are kept intact)
 
+sub is_default_a_coderef { 
+    (reftype($_[0]->{default}) && reftype($_[0]->{default}) eq 'CODE')
+}
+
 sub default { 
-    my $self = shift;
-    if (reftype($self->{default}) && reftype($self->{default}) eq 'CODE') {
+    my ($self, $instance) = @_;
+    if ($instance && $self->is_default_a_coderef) {
         # if the default is a CODE ref, then 
         # we pass in the instance and default
         # can return a value based on that 
         # instance. Somewhat crude, but works.
-        return $self->{default}->(shift);
+        return $self->{default}->($instance);
     }           
     $self->{default};
 }
 
+# slots
+
+sub slots { (shift)->name }
+
 # class association 
 
 sub attach_to_class {
@@ -126,78 +132,114 @@ sub detach_from_class {
     $self->{associated_class} = undef;        
 }
 
-# slot management
-
-sub slot_name { # when attr <-> slot mapping is 1:1
-    my $self = shift;
-    $self->name;
-}
-
-# slot alocation
-
-sub allocate_slots {
-    my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    $meta_instance->add_slot( $self->slot_name ); 
-}
+## Method generation helpers
 
-sub deallocate_slots {
-    my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    $meta_instance->remove_slot( $self->slot_name );
+sub generate_accessor_method {
+    my $self = shift; 
+    my $attr_name  = $self->name;
+    return sub {
+        my $meta_instance = Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))->get_meta_instance;
+        $meta_instance->set_slot_value($_[0], $attr_name, $_[1]) if scalar(@_) == 2;
+        $meta_instance->get_slot_value($_[0], $attr_name);
+    };
 }
 
-## Method generation helpers
+sub generate_accessor_method_inline {
+    my $self          = shift; 
+    my $attr_name     = $self->name;
+    my $meta_instance = $self->associated_class->instance_metaclass;
 
-sub generate_accessor_method {
-    my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;    
-    my $slot_name = $self->slot_name;
+    my $code = eval 'sub {'
+        . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]')  . ' if scalar(@_) == 2; '
+        . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
+    . '}';
+    confess "Could not generate inline accessor because : $@" if $@;
 
-    sub {
-        $meta_instance->set_slot_value($_[0], $slot_name, $_[1]) if scalar(@_) == 2;
-        $meta_instance->get_slot_value($_[0], $slot_name);
-    };
+    return $code;
 }
 
 sub generate_reader_method {
     my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    my $slot_name = $self->slot_name;
-    sub { 
+    my $attr_name  = $self->name;
+    return sub { 
         confess "Cannot assign a value to a read-only accessor" if @_ > 1;
-        $meta_instance->get_slot_value($_[0], $slot_name); 
+        Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))
+                         ->get_meta_instance
+                         ->get_slot_value($_[0], $attr_name); 
     };   
 }
 
+sub generate_reader_method_inline {
+    my $self          = shift; 
+    my $attr_name     = $self->name;
+    my $meta_instance = $self->associated_class->instance_metaclass;
+
+    my $code = eval 'sub {'
+        . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
+        . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
+    . '}';
+    confess "Could not generate inline accessor because : $@" if $@;
+
+    return $code;
+}
+
 sub generate_writer_method {
     my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    my $slot_name = $self->slot_name;
-    sub { 
-        $meta_instance->set_slot_value($_[0], $slot_name, $_[1]);
+    my $attr_name  = $self->name;
+    return sub { 
+        Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))
+                         ->get_meta_instance
+                         ->set_slot_value($_[0], $attr_name, $_[1]);
     };
 }
 
+sub generate_writer_method_inline {
+    my $self          = shift; 
+    my $attr_name     = $self->name;
+    my $meta_instance = $self->associated_class->instance_metaclass;
+
+    my $code = eval 'sub {'
+        . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]')
+    . '}';
+    confess "Could not generate inline accessor because : $@" if $@;
+
+    return $code;
+}
+
 sub generate_predicate_method {
     my $self = shift;
-    my $meta_instance = $self->associated_class->get_meta_instance;
-    my $slot_name = $self->slot_name;
-    sub { 
-        defined $meta_instance->get_slot_value($_[0], $slot_name);
+    my $attr_name  = $self->name;
+    return sub { 
+        defined Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))
+                                 ->get_meta_instance
+                                 ->get_slot_value($_[0], $attr_name) ? 1 : 0;
     };
 }
 
+sub generate_predicate_method_inline {
+    my $self          = shift; 
+    my $attr_name     = $self->name;
+    my $meta_instance = $self->associated_class->instance_metaclass;
+
+    my $code = eval 'sub {'
+        . 'defined ' . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'") . ' ? 1 : 0'
+    . '}';
+    confess "Could not generate inline accessor because : $@" if $@;
+
+    return $code;
+}
+
 sub process_accessors {
-    my ($self, $type, $accessor) = @_;
+    my ($self, $type, $accessor, $generate_as_inline_methods) = @_;
     if (reftype($accessor)) {
         (reftype($accessor) eq 'HASH')
             || confess "bad accessor/reader/writer/predicate format, must be a HASH ref";
-        my ($name, $method) = each %{$accessor};
+        my ($name, $method) = %{$accessor};
         return ($name, Class::MOP::Attribute::Accessor->wrap($method));        
     }
     else {
-        my $generator = $self->can('generate_' . $type . '_method');
+        my $inline_me = ($generate_as_inline_methods && $self->associated_class->instance_metaclass->is_inlinable); 
+        my $generator = $self->can('generate_' . $type . '_method' . ($inline_me ? '_inline' : ''));
         ($generator)
             || confess "There is no method generator for the type='$type'";
         if (my $method = $self->$generator($self->name)) {
@@ -208,24 +250,26 @@ sub process_accessors {
 }
 
 sub install_accessors {
-    my $self  = shift;
-    my $class = $self->associated_class;
+    my $self   = shift;
+    my $inline = shift;
+    my $class  = $self->associated_class;
     
     $class->add_method(
-        $self->process_accessors('accessor' => $self->accessor())
+        $self->process_accessors('accessor' => $self->accessor(), $inline)
     ) if $self->has_accessor();
 
     $class->add_method(            
-        $self->process_accessors('reader' => $self->reader())
+        $self->process_accessors('reader' => $self->reader(), $inline)
     ) if $self->has_reader();
 
     $class->add_method(
-        $self->process_accessors('writer' => $self->writer())
+        $self->process_accessors('writer' => $self->writer(), $inline)
     ) if $self->has_writer();
 
     $class->add_method(
-        $self->process_accessors('predicate' => $self->predicate())
+        $self->process_accessors('predicate' => $self->predicate(), $inline)
     ) if $self->has_predicate();
+    
     return;
 }
 
@@ -448,12 +492,19 @@ passed into C<new>. I think they are pretty much self-explanitory.
 
 =item B<init_arg>
 
+=item B<is_default_a_coderef>
+
 =item B<default (?$instance)>
 
 As noted in the documentation for C<new> above, if the I<default> 
 value is a CODE reference, this accessor will pass a single additional
 argument C<$instance> into it and return the value.
 
+=item B<slots>
+
+Returns a list of slots required by the attribute. This is usually 
+just one, which is the name of the attribute.
+
 =back
 
 =head2 Informational predicates
@@ -527,6 +578,18 @@ use the custom method passed through the constructor.
 
 =back
 
+=over 4
+
+=item B<generate_accessor_method_inline>
+
+=item B<generate_predicate_method_inline>
+
+=item B<generate_reader_method_inline>
+
+=item B<generate_writer_method_inline>
+
+=back
+
 =item B<remove_accessors>
 
 This allows the attribute to remove the method for it's own