Always inline predicate and clearer methods
[gitmo/Moose.git] / lib / Moose / Meta / Method / Accessor.pm
CommitLineData
8ee73eeb 1
2package Moose::Meta::Method::Accessor;
3
4use strict;
5use warnings;
6
df8260e9 7our $VERSION = '0.63';
e606ae5f 8$VERSION = eval $VERSION;
d44714be 9our $AUTHORITY = 'cpan:STEVAN';
8ee73eeb 10
39b3bc94 11use base 'Moose::Meta::Method',
d617b644 12 'Class::MOP::Method::Accessor';
13
18748ad6 14sub _error_thrower {
15 my $self = shift;
16 ( ref $self && $self->associated_attribute ) || $self->SUPER::_error_thrower();
17}
d617b644 18
c9183ffc 19sub _eval_code {
20 my ( $self, $code ) = @_;
21
22 # NOTE:
23 # set up the environment
24 my $attr = $self->associated_attribute;
25 my $attr_name = $attr->name;
cee532a1 26 my $meta = $self,
c9183ffc 27
28 my $type_constraint_obj = $attr->type_constraint;
29 my $type_constraint_name = $type_constraint_obj && $type_constraint_obj->name;
9df136d0 30 my $type_constraint = $type_constraint_obj
31 ? $type_constraint_obj->_compiled_type_constraint
32 : undef;
c9183ffc 33
ae8d63f2 34 #warn "code for $attr_name =>\n" . $code . "\n";
c9183ffc 35 my $sub = eval $code;
cee532a1 36 $self->throw_error("Could not create writer for '$attr_name' because $@ \n code: $code", error => $@, data => $code ) if $@;
c9183ffc 37 return $sub;
38
39}
40
946289d1 41sub generate_accessor_method_inline {
3ccdc84a 42 my $self = $_[0];
26fbace8 43 my $attr = $self->associated_attribute;
3ccdc84a 44 my $attr_name = $attr->name;
45 my $inv = '$_[0]';
e27dfc11 46 my $slot_access = $self->_inline_access($inv, $attr_name);
ac211120 47 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
d617b644 48
c9183ffc 49 $self->_eval_code('sub { ' . "\n"
e27dfc11 50 . $self->_inline_pre_body(@_) . "\n"
e7c06c1e 51 . 'if (scalar(@_) >= 2) {' . "\n"
ac211120 52 . $self->_inline_copy_value . "\n"
e27dfc11 53 . $self->_inline_check_required . "\n"
54 . $self->_inline_check_coercion . "\n"
55 . $self->_inline_check_constraint($value_name) . "\n"
32dd4a95 56 . $self->_inline_store($inv, $value_name) . "\n"
57 . $self->_inline_trigger($inv, $value_name) . "\n"
e27dfc11 58 . ' }' . "\n"
e606ae5f 59 . $self->_inline_check_lazy($inv) . "\n"
e27dfc11 60 . $self->_inline_post_body(@_) . "\n"
61 . 'return ' . $self->_inline_auto_deref($self->_inline_get($inv)) . "\n"
c9183ffc 62 . ' }');
d617b644 63}
64
946289d1 65sub generate_writer_method_inline {
3ccdc84a 66 my $self = $_[0];
26fbace8 67 my $attr = $self->associated_attribute;
3ccdc84a 68 my $attr_name = $attr->name;
69 my $inv = '$_[0]';
70 my $slot_access = $self->_inline_get($inv, $attr_name);
ac211120 71 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
3ccdc84a 72
c9183ffc 73 $self->_eval_code('sub { '
c350159f 74 . $self->_inline_pre_body(@_)
ac211120 75 . $self->_inline_copy_value
d617b644 76 . $self->_inline_check_required
77 . $self->_inline_check_coercion
32dd4a95 78 . $self->_inline_check_constraint($value_name)
79 . $self->_inline_store($inv, $value_name)
80 . $self->_inline_post_body(@_)
81 . $self->_inline_trigger($inv, $value_name)
c9183ffc 82 . ' }');
d617b644 83}
84
946289d1 85sub generate_reader_method_inline {
3ccdc84a 86 my $self = $_[0];
26fbace8 87 my $attr = $self->associated_attribute;
3ccdc84a 88 my $attr_name = $attr->name;
89 my $inv = '$_[0]';
90 my $slot_access = $self->_inline_get($inv, $attr_name);
26fbace8 91
c9183ffc 92 $self->_eval_code('sub {'
c350159f 93 . $self->_inline_pre_body(@_)
cee532a1 94 . $self->_inline_throw_error('"Cannot assign a value to a read-only accessor"', 'data => \@_') . ' if @_ > 1;'
e606ae5f 95 . $self->_inline_check_lazy($inv)
c350159f 96 . $self->_inline_post_body(@_)
3ccdc84a 97 . 'return ' . $self->_inline_auto_deref( $slot_access ) . ';'
c9183ffc 98 . '}');
d617b644 99}
100
ac211120 101sub _inline_copy_value {
102 return '' unless shift->_value_needs_copy;
103 return 'my $val = $_[1];'
104}
105
106sub _value_needs_copy {
107 my $attr = (shift)->associated_attribute;
108 return $attr->should_coerce;
109}
110
51308c23 111sub generate_reader_method { shift->generate_reader_method_inline(@_) }
112sub generate_writer_method { shift->generate_writer_method_inline(@_) }
113sub generate_accessor_method { shift->generate_accessor_method_inline(@_) }
7975db4f 114sub generate_predicate_method { shift->generate_predicate_method_inline(@_) }
115sub generate_clearer_method { shift->generate_clearer_method_inline(@_) }
8ecb1fa0 116
3ccdc84a 117sub _inline_pre_body { '' }
c350159f 118sub _inline_post_body { '' }
119
d617b644 120sub _inline_check_constraint {
97e11ef5 121 my ($self, $value) = @_;
122
123 my $attr = $self->associated_attribute;
8bb7da15 124 my $attr_name = $attr->name;
97e11ef5 125
126 return '' unless $attr->has_type_constraint;
127
8bb7da15 128 my $type_constraint_name = $attr->type_constraint->name;
129
cee532a1 130 qq{\$type_constraint->($value) || } . $self->_inline_throw_error(qq{"Attribute ($attr_name) does not pass the type constraint because: " . \$type_constraint_obj->get_message($value)}, "data => $value") . ";";
d617b644 131}
132
133sub _inline_check_coercion {
97e11ef5 134 my $attr = (shift)->associated_attribute;
135
136 return '' unless $attr->should_coerce;
ac211120 137 return '$val = $attr->type_constraint->coerce($_[1]);'
d617b644 138}
139
140sub _inline_check_required {
cee532a1 141 my $self = shift;
142 my $attr = $self->associated_attribute;
8bb7da15 143
144 my $attr_name = $attr->name;
97e11ef5 145
146 return '' unless $attr->is_required;
cee532a1 147 return qq{(\@_ >= 2) || } . $self->_inline_throw_error(qq{"Attribute ($attr_name) is required, so cannot be set to undef"}) . ';' # defined $_[1] is not good enough
d617b644 148}
149
150sub _inline_check_lazy {
e606ae5f 151 my ($self, $instance) = @_;
152
26fbace8 153 my $attr = $self->associated_attribute;
154
3822d5ee 155 return '' unless $attr->is_lazy;
26fbace8 156
e606ae5f 157 my $slot_access = $self->_inline_access($instance, $attr->name);
3822d5ee 158
e606ae5f 159 my $slot_exists = $self->_inline_has($instance, $attr->name);
3822d5ee 160
161 my $code = 'unless (' . $slot_exists . ') {' . "\n";
162 if ($attr->has_type_constraint) {
97e11ef5 163 if ($attr->has_default || $attr->has_builder) {
164 if ($attr->has_default) {
e606ae5f 165 $code .= ' my $default = $attr->default(' . $instance . ');'."\n";
97e11ef5 166 }
167 elsif ($attr->has_builder) {
3822d5ee 168 $code .= ' my $default;'."\n".
e606ae5f 169 ' if(my $builder = '.$instance.'->can($attr->builder)){ '."\n".
170 ' $default = '.$instance.'->$builder; '. "\n } else {\n" .
171 ' ' . $self->_inline_throw_error(q{sprintf "%s does not support builder method '%s' for attribute '%s'", ref(} . $instance . ') || '.$instance.', $attr->builder, $attr->name') .
172 ';'. "\n }";
3822d5ee 173 }
7c4f0d32 174 $code .= ' $default = $type_constraint_obj->coerce($default);'."\n" if $attr->should_coerce;
175 $code .= ' ($type_constraint->($default))' .
cee532a1 176 ' || ' . $self->_inline_throw_error('"Attribute (" . $attr_name . ") does not pass the type constraint ("' .
177 ' . $type_constraint_name . ") with " . (defined($default) ? overload::StrVal($default) : "undef")' ) . ';'
ab76842e 178 . "\n";
e606ae5f 179 $code .= ' ' . $self->_inline_init_slot($attr, $instance, $slot_access, '$default') . "\n";
97e11ef5 180 }
181 else {
e606ae5f 182 $code .= ' ' . $self->_inline_init_slot($attr, $instance, $slot_access, 'undef') . "\n";
26fbace8 183 }
184
3822d5ee 185 } else {
97e11ef5 186 if ($attr->has_default) {
e606ae5f 187 $code .= ' ' . $self->_inline_init_slot($attr, $instance, $slot_access, ('$attr->default(' . $instance . ')')) . "\n";
97e11ef5 188 }
189 elsif ($attr->has_builder) {
e606ae5f 190 $code .= ' if (my $builder = '.$instance.'->can($attr->builder)) { ' . "\n"
191 . ' ' . $self->_inline_init_slot($attr, $instance, $slot_access, ($instance . '->$builder'))
192 . "\n } else {\n"
193 . ' ' . $self->_inline_throw_error(q{sprintf "%s does not support builder method '%s' for attribute '%s'", ref(} . $instance . ') || '.$instance.', $attr->builder, $attr->name')
194 . ';'. "\n }";
97e11ef5 195 }
196 else {
e606ae5f 197 $code .= ' ' . $self->_inline_init_slot($attr, $instance, $slot_access, 'undef') . "\n";
3822d5ee 198 }
199 }
200 $code .= "}\n";
201 return $code;
d617b644 202}
203
9df136d0 204sub _inline_init_slot {
205 my ($self, $attr, $inv, $slot_access, $value) = @_;
206 if ($attr->has_initializer) {
207 return ('$attr->set_initial_value(' . $inv . ', ' . $value . ');');
208 }
209 else {
210 return ($slot_access . ' = ' . $value . ';');
211 }
212}
d617b644 213
214sub _inline_store {
97e11ef5 215 my ($self, $instance, $value) = @_;
216 my $attr = $self->associated_attribute;
217
218 my $mi = $attr->associated_class->get_meta_instance;
219 my $slot_name = sprintf "'%s'", $attr->slots;
220
d617b644 221 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
97e11ef5 222 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
223 if $attr->is_weak_ref;
d617b644 224 return $code;
225}
226
227sub _inline_trigger {
97e11ef5 228 my ($self, $instance, $value) = @_;
229 my $attr = $self->associated_attribute;
230 return '' unless $attr->has_trigger;
231 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
d617b644 232}
233
234sub _inline_get {
97e11ef5 235 my ($self, $instance) = @_;
236 my $attr = $self->associated_attribute;
237
238 my $mi = $attr->associated_class->get_meta_instance;
239 my $slot_name = sprintf "'%s'", $attr->slots;
d617b644 240
241 return $mi->inline_get_slot_value($instance, $slot_name);
242}
243
e27dfc11 244sub _inline_access {
97e11ef5 245 my ($self, $instance) = @_;
246 my $attr = $self->associated_attribute;
247
248 my $mi = $attr->associated_class->get_meta_instance;
249 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 250
251 return $mi->inline_slot_access($instance, $slot_name);
252}
253
254sub _inline_has {
97e11ef5 255 my ($self, $instance) = @_;
256 my $attr = $self->associated_attribute;
257
258 my $mi = $attr->associated_class->get_meta_instance;
259 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 260
261 return $mi->inline_is_slot_initialized($instance, $slot_name);
262}
263
d617b644 264sub _inline_auto_deref {
265 my ( $self, $ref_value ) = @_;
26fbace8 266 my $attr = $self->associated_attribute;
d617b644 267
39b3bc94 268 return $ref_value unless $attr->should_auto_deref;
d617b644 269
39b3bc94 270 my $type_constraint = $attr->type_constraint;
d617b644 271
272 my $sigil;
273 if ($type_constraint->is_a_type_of('ArrayRef')) {
274 $sigil = '@';
26fbace8 275 }
d617b644 276 elsif ($type_constraint->is_a_type_of('HashRef')) {
277 $sigil = '%';
26fbace8 278 }
d617b644 279 else {
46cb090f 280 $self->throw_error("Can not auto de-reference the type constraint '" . $type_constraint->name . "'", type_constraint => $type_constraint );
d617b644 281 }
282
283 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
284}
8ee73eeb 285
2861;
287
288__END__
289
290=pod
291
39b3bc94 292=head1 NAME
293
ecb59493 294Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 295
296=head1 DESCRIPTION
297
26fbace8 298This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
299responsibility is to generate the accessor methods for attributes. It
ecb59493 300can handle both closure based accessors, as well as inlined source based
26fbace8 301accessors.
ecb59493 302
303This is a fairly new addition to the MOP, but this will play an important
304role in the optimization strategy we are currently following.
305
39b3bc94 306=head1 METHODS
307
308=over 4
309
8ecb1fa0 310=item B<generate_accessor_method>
311
312=item B<generate_reader_method>
313
314=item B<generate_writer_method>
315
7975db4f 316=item B<generate_predicate_method>
317
318=item B<generate_clearer_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
778db3ac 342Copyright 2006-2008 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