switch to hashref-based _eval_closure
[gitmo/Class-MOP.git] / lib / Class / MOP / Method / Constructor.pm
CommitLineData
d90b42a6 1
2package Class::MOP::Method::Constructor;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
8use Scalar::Util 'blessed', 'weaken', 'looks_like_number';
9
34147f49 10our $VERSION = '0.71_01';
d519662a 11$VERSION = eval $VERSION;
d90b42a6 12our $AUTHORITY = 'cpan:STEVAN';
13
565f0cbb 14use base 'Class::MOP::Method::Generated';
d90b42a6 15
16sub new {
17 my $class = shift;
18 my %options = @_;
8d2d4c67 19
ad315b75 20 (blessed $options{metaclass} && $options{metaclass}->isa('Class::MOP::Class'))
21 || confess "You must pass a metaclass instance if you want to inline"
8d2d4c67 22 if $options{is_inline};
23
b38f3848 24 ($options{package_name} && $options{name})
32202ce2 25 || confess "You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT";
b38f3848 26
0bfc85b8 27 my $self = $class->_new(\%options);
d90b42a6 28
8d2d4c67 29 # we don't want this creating
30 # a cycle in the code, if not
d90b42a6 31 # needed
8683db0e 32 weaken($self->{'associated_metaclass'});
d90b42a6 33
565f0cbb 34 $self->initialize_body;
d90b42a6 35
8d2d4c67 36 return $self;
d90b42a6 37}
38
28b97bef 39sub _new {
0bfc85b8 40 my $class = shift;
41 my $options = @_ == 1 ? $_[0] : {@_};
28b97bef 42
43 bless {
44 # from our superclass
45 'body' => undef,
0bfc85b8 46 'package_name' => $options->{package_name},
47 'name' => $options->{name},
28b97bef 48 # specific to this subclass
0bfc85b8 49 'options' => $options->{options} || {},
50 'associated_metaclass' => $options->{metaclass},
51 'is_inline' => ($options->{is_inline} || 0),
28b97bef 52 }, $class;
53}
54
f0de47d9 55sub can_be_inlined { 1 }
56
8d2d4c67 57## accessors
c23184fc 58
8683db0e 59sub options { (shift)->{'options'} }
60sub associated_metaclass { (shift)->{'associated_metaclass'} }
c23184fc 61
565f0cbb 62## cached values ...
d90b42a6 63
8d2d4c67 64sub meta_instance {
565f0cbb 65 my $self = shift;
8683db0e 66 $self->{'meta_instance'} ||= $self->associated_metaclass->get_meta_instance;
565f0cbb 67}
c23184fc 68
8d2d4c67 69sub attributes {
565f0cbb 70 my $self = shift;
8683db0e 71 $self->{'attributes'} ||= [ $self->associated_metaclass->compute_all_applicable_attributes ]
565f0cbb 72}
d90b42a6 73
74## method
75
565f0cbb 76sub initialize_body {
77 my $self = shift;
78 my $method_name = 'generate_constructor_method';
8d2d4c67 79
565f0cbb 80 $method_name .= '_inline' if $self->is_inline;
8d2d4c67 81
8683db0e 82 $self->{'body'} = $self->$method_name;
565f0cbb 83}
84
85sub generate_constructor_method {
2a2b8458 86 return sub { Class::MOP::Class->initialize(shift)->new_object(@_) }
565f0cbb 87}
88
89sub generate_constructor_method_inline {
d90b42a6 90 my $self = shift;
565f0cbb 91
0c6f3280 92 my $close_over = {};
93
d90b42a6 94 my $source = 'sub {';
26ffbb36 95 $source .= "\n" . 'my $class = shift;';
8d2d4c67 96
26ffbb36 97 $source .= "\n" . 'return Class::MOP::Class->initialize($class)->new_object(@_)';
8d2d4c67 98 $source .= "\n" . ' if $class ne \'' . $self->associated_metaclass->name . '\';';
99
26ffbb36 100 $source .= "\n" . 'my $params = @_ == 1 ? $_[0] : {@_};';
101
d90b42a6 102 $source .= "\n" . 'my $instance = ' . $self->meta_instance->inline_create_instance('$class');
8d2d4c67 103 $source .= ";\n" . (join ";\n" => map {
104 $self->_generate_slot_initializer($_)
d90b42a6 105 } 0 .. (@{$self->attributes} - 1));
106 $source .= ";\n" . 'return $instance';
8d2d4c67 107 $source .= ";\n" . '}';
108 warn $source if $self->options->{debug};
109
d90b42a6 110 my $code;
111 {
112 # NOTE:
113 # create the nessecary lexicals
8d2d4c67 114 # to be picked up in the eval
8d2d4c67 115
7f8de9b4 116 $code = $self->_eval_closure(
0c6f3280 117 { '$attrs' => \$self->attributes },
7f8de9b4 118 $source
119 );
d90b42a6 120 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
121 }
565f0cbb 122 return $code;
d90b42a6 123}
124
125sub _generate_slot_initializer {
126 my $self = shift;
127 my $index = shift;
8d2d4c67 128
d90b42a6 129 my $attr = $self->attributes->[$index];
8d2d4c67 130
d90b42a6 131 my $default;
132 if ($attr->has_default) {
133 # NOTE:
134 # default values can either be CODE refs
8d2d4c67 135 # in which case we need to call them. Or
d90b42a6 136 # they can be scalars (strings/numbers)
137 # in which case we can just deal with them
138 # in the code we eval.
139 if ($attr->is_default_a_coderef) {
140 $default = '$attrs->[' . $index . ']->default($instance)';
141 }
142 else {
143 $default = $attr->default;
144 # make sure to quote strings ...
145 unless (looks_like_number($default)) {
146 $default = "'$default'";
147 }
148 }
8d2d4c67 149 } elsif( $attr->has_builder ) {
150 $default = '$instance->'.$attr->builder;
d90b42a6 151 }
8d2d4c67 152
c16c9c1b 153 if ( defined $attr->init_arg ) {
154 return (
26ffbb36 155 'if(exists $params->{\'' . $attr->init_arg . '\'}){' . "\n" .
c16c9c1b 156 $self->meta_instance->inline_set_slot_value(
157 '$instance',
158 ("'" . $attr->name . "'"),
26ffbb36 159 '$params->{\'' . $attr->init_arg . '\'}' ) . "\n" .
c16c9c1b 160 '} ' . (!defined $default ? '' : 'else {' . "\n" .
161 $self->meta_instance->inline_set_slot_value(
162 '$instance',
163 ("'" . $attr->name . "'"),
164 $default ) . "\n" .
165 '}')
166 );
167 } elsif ( defined $default ) {
168 return (
169 $self->meta_instance->inline_set_slot_value(
170 '$instance',
171 ("'" . $attr->name . "'"),
172 $default ) . "\n"
173 );
174 } else { return '' }
d90b42a6 175}
176
1771;
178
d90b42a6 179__END__
180
181=pod
182
8d2d4c67 183=head1 NAME
d90b42a6 184
185Class::MOP::Method::Constructor - Method Meta Object for constructors
186
187=head1 SYNOPSIS
188
96e38ba6 189 use Class::MOP::Method::Constructor;
8d2d4c67 190
96e38ba6 191 my $constructor = Class::MOP::Method::Constructor->new(
8d2d4c67 192 metaclass => $metaclass,
96e38ba6 193 options => {
194 debug => 1, # this is all for now
8d2d4c67 195 },
96e38ba6 196 );
8d2d4c67 197
96e38ba6 198 # calling the constructor ...
b7045e66 199 $constructor->body->execute($metaclass->name, %params);
8d2d4c67 200
d90b42a6 201=head1 DESCRIPTION
202
8d2d4c67 203This is a subclass of C<Class::MOP::Method> which deals with
127d39a7 204class constructors. This is used when making a class immutable
205to generate an optimized constructor.
96e38ba6 206
d90b42a6 207=head1 METHODS
208
209=over 4
210
96e38ba6 211=item B<new (metaclass => $meta, options => \%options)>
d90b42a6 212
96e38ba6 213=item B<options>
214
215This returns the options HASH which is passed into C<new>.
216
217=item B<associated_metaclass>
218
219This returns the metaclass which is passed into C<new>.
c23184fc 220
d90b42a6 221=item B<attributes>
222
8d2d4c67 223This returns the list of attributes which are associated with the
96e38ba6 224metaclass which is passed into C<new>.
225
d90b42a6 226=item B<meta_instance>
227
8d2d4c67 228This returns the meta instance which is associated with the
96e38ba6 229metaclass which is passed into C<new>.
c23184fc 230
96e38ba6 231=item B<is_inline>
232
8d2d4c67 233This returns a boolean, but since constructors are very rarely
96e38ba6 234not inlined, this always returns true for now.
d90b42a6 235
f0de47d9 236=item B<can_be_inlined>
237
238This method always returns true in this class. It exists so that
239subclasses (like in Moose) can override and do some sort of checking
240to determine whether or not inlining the constructor is safe.
241
565f0cbb 242=item B<initialize_body>
d90b42a6 243
8d2d4c67 244This creates the code reference for the constructor itself.
96e38ba6 245
d90b42a6 246=back
247
127d39a7 248=head2 Method Generators
565f0cbb 249
250=over 4
251
252=item B<generate_constructor_method>
253
254=item B<generate_constructor_method_inline>
255
256=back
257
d90b42a6 258=head1 AUTHORS
259
260Stevan Little E<lt>stevan@iinteractive.comE<gt>
261
262=head1 COPYRIGHT AND LICENSE
263
69e3ab0a 264Copyright 2006-2008 by Infinity Interactive, Inc.
d90b42a6 265
266L<http://www.iinteractive.com>
267
268This library is free software; you can redistribute it and/or modify
8d2d4c67 269it under the same terms as Perl itself.
d90b42a6 270
271=cut
272