break out method generation into an _eval_closure method
[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
d90b42a6 92 my $source = 'sub {';
26ffbb36 93 $source .= "\n" . 'my $class = shift;';
8d2d4c67 94
26ffbb36 95 $source .= "\n" . 'return Class::MOP::Class->initialize($class)->new_object(@_)';
8d2d4c67 96 $source .= "\n" . ' if $class ne \'' . $self->associated_metaclass->name . '\';';
97
26ffbb36 98 $source .= "\n" . 'my $params = @_ == 1 ? $_[0] : {@_};';
99
d90b42a6 100 $source .= "\n" . 'my $instance = ' . $self->meta_instance->inline_create_instance('$class');
8d2d4c67 101 $source .= ";\n" . (join ";\n" => map {
102 $self->_generate_slot_initializer($_)
d90b42a6 103 } 0 .. (@{$self->attributes} - 1));
104 $source .= ";\n" . 'return $instance';
8d2d4c67 105 $source .= ";\n" . '}';
106 warn $source if $self->options->{debug};
107
d90b42a6 108 my $code;
109 {
110 # NOTE:
111 # create the nessecary lexicals
8d2d4c67 112 # to be picked up in the eval
8d2d4c67 113
7f8de9b4 114 $code = $self->_eval_closure(
115 q{my $attrs = $self->attributes;},
116 $source
117 );
d90b42a6 118 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
119 }
565f0cbb 120 return $code;
d90b42a6 121}
122
123sub _generate_slot_initializer {
124 my $self = shift;
125 my $index = shift;
8d2d4c67 126
d90b42a6 127 my $attr = $self->attributes->[$index];
8d2d4c67 128
d90b42a6 129 my $default;
130 if ($attr->has_default) {
131 # NOTE:
132 # default values can either be CODE refs
8d2d4c67 133 # in which case we need to call them. Or
d90b42a6 134 # they can be scalars (strings/numbers)
135 # in which case we can just deal with them
136 # in the code we eval.
137 if ($attr->is_default_a_coderef) {
138 $default = '$attrs->[' . $index . ']->default($instance)';
139 }
140 else {
141 $default = $attr->default;
142 # make sure to quote strings ...
143 unless (looks_like_number($default)) {
144 $default = "'$default'";
145 }
146 }
8d2d4c67 147 } elsif( $attr->has_builder ) {
148 $default = '$instance->'.$attr->builder;
d90b42a6 149 }
8d2d4c67 150
c16c9c1b 151 if ( defined $attr->init_arg ) {
152 return (
26ffbb36 153 'if(exists $params->{\'' . $attr->init_arg . '\'}){' . "\n" .
c16c9c1b 154 $self->meta_instance->inline_set_slot_value(
155 '$instance',
156 ("'" . $attr->name . "'"),
26ffbb36 157 '$params->{\'' . $attr->init_arg . '\'}' ) . "\n" .
c16c9c1b 158 '} ' . (!defined $default ? '' : 'else {' . "\n" .
159 $self->meta_instance->inline_set_slot_value(
160 '$instance',
161 ("'" . $attr->name . "'"),
162 $default ) . "\n" .
163 '}')
164 );
165 } elsif ( defined $default ) {
166 return (
167 $self->meta_instance->inline_set_slot_value(
168 '$instance',
169 ("'" . $attr->name . "'"),
170 $default ) . "\n"
171 );
172 } else { return '' }
d90b42a6 173}
174
1751;
176
d90b42a6 177__END__
178
179=pod
180
8d2d4c67 181=head1 NAME
d90b42a6 182
183Class::MOP::Method::Constructor - Method Meta Object for constructors
184
185=head1 SYNOPSIS
186
96e38ba6 187 use Class::MOP::Method::Constructor;
8d2d4c67 188
96e38ba6 189 my $constructor = Class::MOP::Method::Constructor->new(
8d2d4c67 190 metaclass => $metaclass,
96e38ba6 191 options => {
192 debug => 1, # this is all for now
8d2d4c67 193 },
96e38ba6 194 );
8d2d4c67 195
96e38ba6 196 # calling the constructor ...
b7045e66 197 $constructor->body->execute($metaclass->name, %params);
8d2d4c67 198
d90b42a6 199=head1 DESCRIPTION
200
8d2d4c67 201This is a subclass of C<Class::MOP::Method> which deals with
127d39a7 202class constructors. This is used when making a class immutable
203to generate an optimized constructor.
96e38ba6 204
d90b42a6 205=head1 METHODS
206
207=over 4
208
96e38ba6 209=item B<new (metaclass => $meta, options => \%options)>
d90b42a6 210
96e38ba6 211=item B<options>
212
213This returns the options HASH which is passed into C<new>.
214
215=item B<associated_metaclass>
216
217This returns the metaclass which is passed into C<new>.
c23184fc 218
d90b42a6 219=item B<attributes>
220
8d2d4c67 221This returns the list of attributes which are associated with the
96e38ba6 222metaclass which is passed into C<new>.
223
d90b42a6 224=item B<meta_instance>
225
8d2d4c67 226This returns the meta instance which is associated with the
96e38ba6 227metaclass which is passed into C<new>.
c23184fc 228
96e38ba6 229=item B<is_inline>
230
8d2d4c67 231This returns a boolean, but since constructors are very rarely
96e38ba6 232not inlined, this always returns true for now.
d90b42a6 233
f0de47d9 234=item B<can_be_inlined>
235
236This method always returns true in this class. It exists so that
237subclasses (like in Moose) can override and do some sort of checking
238to determine whether or not inlining the constructor is safe.
239
565f0cbb 240=item B<initialize_body>
d90b42a6 241
8d2d4c67 242This creates the code reference for the constructor itself.
96e38ba6 243
d90b42a6 244=back
245
127d39a7 246=head2 Method Generators
565f0cbb 247
248=over 4
249
250=item B<generate_constructor_method>
251
252=item B<generate_constructor_method_inline>
253
254=back
255
d90b42a6 256=head1 AUTHORS
257
258Stevan Little E<lt>stevan@iinteractive.comE<gt>
259
260=head1 COPYRIGHT AND LICENSE
261
69e3ab0a 262Copyright 2006-2008 by Infinity Interactive, Inc.
d90b42a6 263
264L<http://www.iinteractive.com>
265
266This library is free software; you can redistribute it and/or modify
8d2d4c67 267it under the same terms as Perl itself.
d90b42a6 268
269=cut
270