yay
[gitmo/Moose.git] / lib / Moose / Meta / Method / Accessor.pm
CommitLineData
8ee73eeb 1
2package Moose::Meta::Method::Accessor;
3
4use strict;
5use warnings;
6
39b3bc94 7use Carp 'confess';
8
3822d5ee 9our $VERSION = '0.08';
d44714be 10our $AUTHORITY = 'cpan:STEVAN';
8ee73eeb 11
39b3bc94 12use base 'Moose::Meta::Method',
d617b644 13 'Class::MOP::Method::Accessor';
14
946289d1 15## Inline method generators
d617b644 16
946289d1 17sub generate_accessor_method_inline {
3ccdc84a 18 my $self = $_[0];
26fbace8 19 my $attr = $self->associated_attribute;
3ccdc84a 20 my $attr_name = $attr->name;
21 my $inv = '$_[0]';
e27dfc11 22 my $slot_access = $self->_inline_access($inv, $attr_name);
ac211120 23 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
d617b644 24
7c4f0d32 25 my $type_constraint_obj = $attr->type_constraint;
26 my $type_constraint_name = $type_constraint_obj && $type_constraint_obj->name;
27
e27dfc11 28 my $code = 'sub { ' . "\n"
29 . $self->_inline_pre_body(@_) . "\n"
30 . 'if (scalar(@_) == 2) {' . "\n"
ac211120 31 . $self->_inline_copy_value . "\n"
e27dfc11 32 . $self->_inline_check_required . "\n"
33 . $self->_inline_check_coercion . "\n"
34 . $self->_inline_check_constraint($value_name) . "\n"
35 . $self->_inline_store($inv, $value_name) . "\n"
36 . $self->_inline_trigger($inv, $value_name) . "\n"
37 . ' }' . "\n"
38 . $self->_inline_check_lazy . "\n"
39 . $self->_inline_post_body(@_) . "\n"
40 . 'return ' . $self->_inline_auto_deref($self->_inline_get($inv)) . "\n"
d617b644 41 . ' }';
26fbace8 42
d617b644 43 # NOTE:
44 # set up the environment
26fbace8 45 my $type_constraint = $attr->type_constraint
7c4f0d32 46 ? (
47 $attr->type_constraint->has_hand_optimized_type_constraint
48 ? $attr->type_constraint->hand_optimized_type_constraint
49 : $attr->type_constraint->_compiled_type_constraint
50 )
d617b644 51 : undef;
26fbace8 52
d617b644 53 my $sub = eval $code;
54 confess "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
26fbace8 55 return $sub;
d617b644 56}
57
946289d1 58sub generate_writer_method_inline {
3ccdc84a 59 my $self = $_[0];
26fbace8 60 my $attr = $self->associated_attribute;
3ccdc84a 61 my $attr_name = $attr->name;
62 my $inv = '$_[0]';
63 my $slot_access = $self->_inline_get($inv, $attr_name);
ac211120 64 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
3ccdc84a 65
d617b644 66 my $code = 'sub { '
c350159f 67 . $self->_inline_pre_body(@_)
ac211120 68 . $self->_inline_copy_value
d617b644 69 . $self->_inline_check_required
70 . $self->_inline_check_coercion
26fbace8 71 . $self->_inline_check_constraint($value_name)
72 . $self->_inline_store($inv, $value_name)
73 . $self->_inline_post_body(@_)
74 . $self->_inline_trigger($inv, $value_name)
d617b644 75 . ' }';
26fbace8 76
d617b644 77 # NOTE:
78 # set up the environment
26fbace8 79 my $type_constraint = $attr->type_constraint
d617b644 80 ? $attr->type_constraint->_compiled_type_constraint
26fbace8 81 : undef;
82
d617b644 83 my $sub = eval $code;
84 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
26fbace8 85 return $sub;
d617b644 86}
87
946289d1 88sub generate_reader_method_inline {
3ccdc84a 89 my $self = $_[0];
26fbace8 90 my $attr = $self->associated_attribute;
3ccdc84a 91 my $attr_name = $attr->name;
92 my $inv = '$_[0]';
93 my $slot_access = $self->_inline_get($inv, $attr_name);
26fbace8 94
d617b644 95 my $code = 'sub {'
c350159f 96 . $self->_inline_pre_body(@_)
d617b644 97 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
98 . $self->_inline_check_lazy
c350159f 99 . $self->_inline_post_body(@_)
3ccdc84a 100 . 'return ' . $self->_inline_auto_deref( $slot_access ) . ';'
d617b644 101 . '}';
26fbace8 102
7bffa230 103 # NOTE:
104 # set up the environment
26fbace8 105 my $type_constraint = $attr->type_constraint
7bffa230 106 ? $attr->type_constraint->_compiled_type_constraint
26fbace8 107 : undef;
108
d617b644 109 my $sub = eval $code;
110 confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
111 return $sub;
112}
113
ac211120 114sub _inline_copy_value {
115 return '' unless shift->_value_needs_copy;
116 return 'my $val = $_[1];'
117}
118
119sub _value_needs_copy {
120 my $attr = (shift)->associated_attribute;
121 return $attr->should_coerce;
122}
123
51308c23 124sub generate_reader_method { shift->generate_reader_method_inline(@_) }
125sub generate_writer_method { shift->generate_writer_method_inline(@_) }
126sub generate_accessor_method { shift->generate_accessor_method_inline(@_) }
8ecb1fa0 127
3ccdc84a 128sub _inline_pre_body { '' }
c350159f 129sub _inline_post_body { '' }
130
d617b644 131sub _inline_check_constraint {
97e11ef5 132 my ($self, $value) = @_;
133
134 my $attr = $self->associated_attribute;
135
136 return '' unless $attr->has_type_constraint;
137
138 # FIXME
139 # This sprintf is insanely annoying, we should
140 # fix it someday - SL
141 return sprintf <<'EOF', $value, $value, $value, $value, $value, $value, $value
7c4f0d32 142$type_constraint->(%s)
143 || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("
144 . $type_constraint_name . ") with "
8ac5969a 145 . (defined(%s) ? (Scalar::Util::blessed(%s) && overload::Overloaded(%s) ? overload::StrVal(%s) : %s) : "undef")
d617b644 146 if defined(%s);
147EOF
148}
149
150sub _inline_check_coercion {
97e11ef5 151 my $attr = (shift)->associated_attribute;
152
153 return '' unless $attr->should_coerce;
ac211120 154 return '$val = $attr->type_constraint->coerce($_[1]);'
d617b644 155}
156
157sub _inline_check_required {
97e11ef5 158 my $attr = (shift)->associated_attribute;
159
160 return '' unless $attr->is_required;
d617b644 161 return 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
162}
163
164sub _inline_check_lazy {
3ccdc84a 165 my $self = $_[0];
26fbace8 166 my $attr = $self->associated_attribute;
167
3822d5ee 168 return '' unless $attr->is_lazy;
26fbace8 169
3ccdc84a 170 my $inv = '$_[0]';
e27dfc11 171 my $slot_access = $self->_inline_access($inv, $attr->name);
3822d5ee 172
e27dfc11 173 my $slot_exists = $self->_inline_has($inv, $attr->name);
3822d5ee 174
175 my $code = 'unless (' . $slot_exists . ') {' . "\n";
176 if ($attr->has_type_constraint) {
97e11ef5 177 if ($attr->has_default || $attr->has_builder) {
178 if ($attr->has_default) {
3822d5ee 179 $code .= ' my $default = $attr->default(' . $inv . ');'."\n";
97e11ef5 180 }
181 elsif ($attr->has_builder) {
3822d5ee 182 $code .= ' my $default;'."\n".
183 ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
184 ' $default = '.$inv.'->$builder; '. "\n } else {\n" .
185 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
186 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
187 }
7c4f0d32 188 $code .= ' $default = $type_constraint_obj->coerce($default);'."\n" if $attr->should_coerce;
189 $code .= ' ($type_constraint->($default))' .
190 ' || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("' .
191 ' . $type_constraint_name . ") with " . (defined($default) ? (Scalar::Util::blessed($default) && overload::Overloaded($default) ? overload::StrVal($default) : $default) : "undef")' .
3822d5ee 192 ' if defined($default);' . "\n" .
193 ' ' . $slot_access . ' = $default; ' . "\n";
97e11ef5 194 }
195 else {
3822d5ee 196 $code .= ' ' . $slot_access . " = undef; \n";
26fbace8 197 }
198
3822d5ee 199 } else {
97e11ef5 200 if ($attr->has_default) {
3822d5ee 201 $code .= ' '.$slot_access.' = $attr->default(' . $inv . ');'."\n";
97e11ef5 202 }
203 elsif ($attr->has_builder) {
3822d5ee 204 $code .= ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
205 ' '.$slot_access.' = '.$inv.'->$builder; '. "\n } else {\n" .
206 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
207 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
97e11ef5 208 }
209 else {
3822d5ee 210 $code .= ' ' . $slot_access . " = undef; \n";
211 }
212 }
213 $code .= "}\n";
214 return $code;
d617b644 215}
216
217
218sub _inline_store {
97e11ef5 219 my ($self, $instance, $value) = @_;
220 my $attr = $self->associated_attribute;
221
222 my $mi = $attr->associated_class->get_meta_instance;
223 my $slot_name = sprintf "'%s'", $attr->slots;
224
d617b644 225 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
97e11ef5 226 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
227 if $attr->is_weak_ref;
d617b644 228 return $code;
229}
230
231sub _inline_trigger {
97e11ef5 232 my ($self, $instance, $value) = @_;
233 my $attr = $self->associated_attribute;
234 return '' unless $attr->has_trigger;
235 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
d617b644 236}
237
238sub _inline_get {
97e11ef5 239 my ($self, $instance) = @_;
240 my $attr = $self->associated_attribute;
241
242 my $mi = $attr->associated_class->get_meta_instance;
243 my $slot_name = sprintf "'%s'", $attr->slots;
d617b644 244
245 return $mi->inline_get_slot_value($instance, $slot_name);
246}
247
e27dfc11 248sub _inline_access {
97e11ef5 249 my ($self, $instance) = @_;
250 my $attr = $self->associated_attribute;
251
252 my $mi = $attr->associated_class->get_meta_instance;
253 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 254
255 return $mi->inline_slot_access($instance, $slot_name);
256}
257
258sub _inline_has {
97e11ef5 259 my ($self, $instance) = @_;
260 my $attr = $self->associated_attribute;
261
262 my $mi = $attr->associated_class->get_meta_instance;
263 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 264
265 return $mi->inline_is_slot_initialized($instance, $slot_name);
266}
267
d617b644 268sub _inline_auto_deref {
269 my ( $self, $ref_value ) = @_;
26fbace8 270 my $attr = $self->associated_attribute;
d617b644 271
39b3bc94 272 return $ref_value unless $attr->should_auto_deref;
d617b644 273
39b3bc94 274 my $type_constraint = $attr->type_constraint;
d617b644 275
276 my $sigil;
277 if ($type_constraint->is_a_type_of('ArrayRef')) {
278 $sigil = '@';
26fbace8 279 }
d617b644 280 elsif ($type_constraint->is_a_type_of('HashRef')) {
281 $sigil = '%';
26fbace8 282 }
d617b644 283 else {
284 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
285 }
286
287 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
288}
8ee73eeb 289
2901;
291
292__END__
293
294=pod
295
39b3bc94 296=head1 NAME
297
ecb59493 298Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 299
300=head1 DESCRIPTION
301
26fbace8 302This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
303responsibility is to generate the accessor methods for attributes. It
ecb59493 304can handle both closure based accessors, as well as inlined source based
26fbace8 305accessors.
ecb59493 306
307This is a fairly new addition to the MOP, but this will play an important
308role in the optimization strategy we are currently following.
309
39b3bc94 310=head1 METHODS
311
312=over 4
313
8ecb1fa0 314=item B<generate_accessor_method>
315
316=item B<generate_reader_method>
317
318=item B<generate_writer_method>
319
39b3bc94 320=item B<generate_accessor_method_inline>
321
39b3bc94 322=item B<generate_reader_method_inline>
323
39b3bc94 324=item B<generate_writer_method_inline>
325
326=back
327
328=head1 BUGS
329
26fbace8 330All complex software has bugs lurking in it, and this module is no
39b3bc94 331exception. If you find a bug please either email me, or add the bug
332to cpan-RT.
333
334=head1 AUTHOR
335
336Stevan Little E<lt>stevan@iinteractive.comE<gt>
337
338Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
339
340=head1 COPYRIGHT AND LICENSE
341
b77fdbed 342Copyright 2006, 2007 by Infinity Interactive, Inc.
39b3bc94 343
344L<http://www.iinteractive.com>
345
346This library is free software; you can redistribute it and/or modify
347it under the same terms as Perl itself.
348
51308c23 349=cut