Add a benchmark for new_object()
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Constructor.pm
CommitLineData
fc1d8369 1package Mouse::Meta::Method::Constructor;
3821b191 2use Mouse::Util qw(:meta); # enables strict and warnings
fc1d8369 3
f031f4d5 4sub _inline_slot{
f8cbb121 5 my(undef, $self_var, $attr_name) = @_;
6 return sprintf '%s->{q{%s}}', $self_var, $attr_name;
7}
8
380e1cd7 9sub _generate_constructor {
2a464664 10 my ($class, $metaclass, $args) = @_;
24ad3f66 11
f031f4d5 12 my $associated_metaclass_name = $metaclass->name;
13
3db78d2a 14 my $buildall = $class->_generate_BUILDALL($metaclass);
f031f4d5 15 my $buildargs = $class->_generate_BUILDARGS($metaclass);
ad087d11 16
a3a09e38 17 my $source = sprintf(<<'EOT', __LINE__, __FILE__, $metaclass->name, $buildargs, $buildall);
18#line %d %s
19 package %s;
20 sub {
21 my $class = shift;
22 return $class->Mouse::Object::new(@_)
23 if $class ne __PACKAGE__;
0bfc7290 24 # BUILDARGS
a3a09e38 25 %s;
26 my $instance = bless {}, $class;
27 $metaclass->_initialize_object($instance, $args, 0);
0bfc7290 28 # BUILDALL
a3a09e38 29 %s;
30 return $instance;
2a464664 31 }
a3a09e38 32EOT
0bfc7290 33 #warn $source;
a3a09e38 34 my $body;
ad087d11 35 my $e = do{
36 local $@;
a3a09e38 37 $body = eval $source;
ad087d11 38 $@;
39 };
40 die $e if $e;
a3a09e38 41 return $body;
fc1d8369 42}
43
a3a09e38 44sub _generate_initialize_object {
45 my ($method_class, $metaclass) = @_;
46 my @attrs = $metaclass->get_all_attributes;
47
48 my @checks = map { $_ && $_->_compiled_type_constraint }
49 map { $_->type_constraint } @attrs;
50
fc1d8369 51 my @res;
9df6f0cd 52
2efc0af1 53 my $has_triggers;
fb4ddd88 54 my $strict = $metaclass->strict_constructor;
e128626c 55
8801a6e6 56 if($strict){
e128626c 57 push @res, 'my $used = 0;';
58 }
2efc0af1 59
a3a09e38 60 for my $index (0 .. @attrs - 1) {
0bfc7290 61 my $code = '';
62
a3a09e38 63 my $attr = $attrs[$index];
fc1d8369 64 my $key = $attr->name;
9df6f0cd 65
0bfc7290 66 my $init_arg = $attr->init_arg;
67 my $type_constraint = $attr->type_constraint;
8aba926d 68 my $is_weak_ref = $attr->is_weak_ref;
0bfc7290 69 my $need_coercion;
c91862e8 70
f031f4d5 71 my $instance_slot = $method_class->_inline_slot('$instance', $key);
0bfc7290 72 my $attr_var = "\$attrs[$index]";
73 my $constraint_var;
c91862e8 74
0bfc7290 75 if(defined $type_constraint){
76 $constraint_var = "$attr_var\->{type_constraint}";
77 $need_coercion = ($attr->should_coerce && $type_constraint->has_coercion);
78 }
c91862e8 79
0bfc7290 80 $code .= "# initialize $key\n";
81
82 my $post_process = '';
83 if(defined $type_constraint){
a3a09e38 84 $post_process .= "\$checks[$index]->($instance_slot)\n";
da23cd4a 85 $post_process .= " or $attr_var->_throw_type_constraint_error($instance_slot, $constraint_var);\n";
0bfc7290 86 }
8aba926d 87 if($is_weak_ref){
a3a09e38 88 $post_process = "Scalar::Util::weaken($instance_slot) "
89 . "if ref $instance_slot;\n";
0bfc7290 90 }
c91862e8 91
a3a09e38 92 # build cde for an attribute
0bfc7290 93 if (defined $init_arg) {
94 my $value = "\$args->{q{$init_arg}}";
c91862e8 95
0bfc7290 96 $code .= "if (exists $value) {\n";
97
98 if($need_coercion){
620c3203 99 $value = "$constraint_var->coerce($value)";
41cdacce 100 }
c91862e8 101
f031f4d5 102 $code .= "$instance_slot = $value;\n";
0bfc7290 103 $code .= $post_process;
104
9df6f0cd 105 if ($attr->has_trigger) {
2efc0af1 106 $has_triggers++;
620c3203 107 $code .= "push \@triggers, [$attr_var\->{trigger}, $instance_slot];\n";
fc1d8369 108 }
c91862e8 109
8801a6e6 110 if ($strict){
111 $code .= '++$used;' . "\n";
e128626c 112 }
113
114 $code .= "\n} else {\n"; # $value exists
9df6f0cd 115 }
c91862e8 116
9df6f0cd 117 if ($attr->has_default || $attr->has_builder) {
118 unless ($attr->is_lazy) {
119 my $default = $attr->default;
120 my $builder = $attr->builder;
e8ba7b26 121
0bfc7290 122 my $value;
123 if (defined($builder)) {
124 $value = "\$instance->$builder()";
ffbbf459 125 }
126 elsif (ref($default) eq 'CODE') {
0bfc7290 127 $value = "$attr_var\->{default}->(\$instance)";
ffbbf459 128 }
0bfc7290 129 elsif (defined($default)) {
130 $value = "$attr_var\->{default}";
ffbbf459 131 }
132 else {
0bfc7290 133 $value = 'undef';
ffbbf459 134 }
e8ba7b26 135
0bfc7290 136 if($need_coercion){
137 $value = "$constraint_var->coerce($value)";
9df6f0cd 138 }
9df6f0cd 139
f031f4d5 140 $code .= "$instance_slot = $value;\n";
a3a09e38 141 $code .= $post_process;
fc1d8369 142 }
713a2a05 143 }
9df6f0cd 144 elsif ($attr->is_required) {
a3a09e38 145 $code .= "\$meta->throw_error('Attribute ($key) is required')";
146 $code .= " unless \$is_cloning;\n";
9df6f0cd 147 }
148
0bfc7290 149 $code .= "}\n" if defined $init_arg;
9df6f0cd 150
fc1d8369 151 push @res, $code;
152 }
9df6f0cd 153
8801a6e6 154 if($strict){
e128626c 155 push @res, q{if($used < keys %{$args})}
a3a09e38 156 . q{{ $meta->_report_unknown_args(\@attrs, $args) }};
e128626c 157 }
158
2efc0af1 159 if($metaclass->is_anon_class){
a3a09e38 160 push @res, q{$instance->{__METACLASS__} = $meta;};
2efc0af1 161 }
162
163 if($has_triggers){
164 unshift @res, q{my @triggers;};
e128626c 165 push @res, q{$_->[0]->($instance, $_->[1]) for @triggers;};
2efc0af1 166 }
167
a3a09e38 168 my $source = sprintf <<'EOT', __LINE__, __FILE__, $metaclass->name, join "\n", @res;
169#line %d %s
170 package %s;
171 sub {
172 my($meta, $instance, $args, $is_cloning) = @_;
173 %s;
174 return $instance;
175 }
176EOT
177 warn $source if $ENV{MOUSE_DEBUG};
178 my $body;
179 my $e = do {
180 local $@;
181 $body = eval $source;
182 $@;
183 };
184 die $e if $e;
185 return $body;
fc1d8369 186}
187
188sub _generate_BUILDARGS {
0bfc7290 189 my(undef, $metaclass) = @_;
9dcd7d23 190
0bfc7290 191 my $class = $metaclass->name;
192 if ( $class->can('BUILDARGS') && $class->can('BUILDARGS') != \&Mouse::Object::BUILDARGS ) {
53d4053e 193 return 'my $args = $class->BUILDARGS(@_)';
9dcd7d23 194 }
195
196 return <<'...';
53d4053e 197 my $args;
fc1d8369 198 if ( scalar @_ == 1 ) {
c9aefe26 199 ( ref( $_[0] ) eq 'HASH' )
fc1d8369 200 || Carp::confess "Single parameters to new() must be a HASH ref";
53d4053e 201 $args = +{ %{ $_[0] } };
fc1d8369 202 }
203 else {
53d4053e 204 $args = +{@_};
fc1d8369 205 }
fc1d8369 206...
207}
208
209sub _generate_BUILDALL {
0bfc7290 210 my (undef, $metaclass) = @_;
7ca5c5fb 211
2efc0af1 212 return '' unless $metaclass->name->can('BUILD');
fc1d8369 213
7ca5c5fb 214 my @code;
215 for my $class ($metaclass->linearized_isa) {
a5c683f6 216 if (Mouse::Util::get_code_ref($class, 'BUILD')) {
7ca5c5fb 217 unshift @code, qq{${class}::BUILD(\$instance, \$args);};
fc1d8369 218 }
219 }
220 return join "\n", @code;
221}
222
2231;
0126c27c 224__END__
a25ca8d6 225
226=head1 NAME
227
228Mouse::Meta::Method::Constructor - A Mouse method generator for constructors
229
230=head1 VERSION
231
43c1bb1a 232This document describes Mouse version 0.71
a25ca8d6 233
234=head1 SEE ALSO
235
236L<Moose::Meta::Method::Constructor>
237
238=cut