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