performance tuning
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Constructor.pm
1 package Mouse::Meta::Method::Constructor;
2 use strict;
3 use warnings;
4
5 sub generate_constructor_method_inline {
6     my ($class, $meta) = @_;
7
8     my @attrs = $meta->compute_all_applicable_attributes; # this one is using by evaled code
9     my $buildall = $class->_generate_BUILDALL($meta);
10     my $buildargs = $class->_generate_BUILDARGS();
11     my $processattrs = $class->_generate_processattrs($meta, \@attrs);
12
13     my $code = <<"...";
14     sub {
15         my \$class = shift;
16         my \$args = $buildargs;
17         my \$instance = bless {}, \$class;
18         $processattrs;
19         $buildall;
20         return \$instance;
21     }
22 ...
23
24     warn $code if $ENV{DEBUG};
25
26     local $@;
27     my $res = eval $code;
28     die $@ if $@;
29     $res;
30 }
31
32 sub _generate_processattrs {
33     my ($class, $meta, $attrs) = @_;
34     my @res;
35     for my $index (0..scalar(@$attrs)-1) {
36         my $attr = $attrs->[$index];
37         my $from = $attr->init_arg;
38         my $key  = $attr->name;
39         my $part1 = do {
40             my @code;
41             if ($attr->should_coerce) {
42                 push @code, "\$args->{\$from} = \$attr->coerce_constraint( \$args->{\$from} );";
43             }
44             if ($attr->has_type_constraint) {
45                 push @code, "\$attr->verify_type_constraint( \$args->{\$from} );";
46             }
47             push @code, "\$instance->{\$key} = \$args->{\$from};";
48             if ($attr->is_weak_ref) {
49                 push @code, "weaken( \$instance->{\$key} ) if ref( \$instance->{\$key} );";
50             }
51             if ( $attr->has_trigger ) {
52                 push @code, "\$attr->trigger->( \$instance, \$args->{\$from}, \$attr );";
53             }
54             join "\n", @code;
55         };
56         my $part2 = do {
57             my @code;
58             if ( $attr->has_default || $attr->has_builder ) {
59                 unless ( $attr->is_lazy ) {
60                     my $default = $attr->default;
61                     my $builder = $attr->builder;
62                     if ($attr->has_builder) {
63                         push @code, "my \$value = \$instance->$builder;";
64                     } elsif (ref($default) eq 'CODE') {
65                         push @code, "my \$value = \$attr->default()->();";
66                     } else {
67                         push @code, "my \$value = \$attr->default();";
68                     }
69                     if ($attr->should_coerce) {
70                         push @code, "\$value = \$attr->coerce_constraint(\$value);";
71                     }
72                     if ($attr->has_type_constraint) {
73                         push @code, "\$attr->verify_type_constraint(\$value);";
74                     }
75                     push @code, "\$instance->{\$key} = \$value;";
76                     if ($attr->is_weak_ref) {
77                         push @code, "weaken( \$instance->{\$key} ) if ref( \$instance->{\$key} );";
78                     }
79                 }
80                 join "\n", @code;
81             }
82             else {
83                 if ( $attr->is_required ) {
84                     q{Carp::confess("Attribute (} . $attr->name . q{) is required");};
85                 } else {
86                     ""
87                 }
88             }
89         };
90         my $code = <<"...";
91             {
92                 my \$attr = \$attrs[$index];
93                 my \$from = '$from';
94                 my \$key  = '$key';
95                 if (defined(\$from) && exists(\$args->{\$from})) {
96                     $part1;
97                 } else {
98                     $part2;
99                 }
100             }
101 ...
102         push @res, $code;
103     }
104     return join "\n", @res;
105 }
106
107 sub _generate_BUILDARGS {
108     <<'...';
109     do {
110         if ( scalar @_ == 1 ) {
111             if ( defined $_[0] ) {
112                 ( ref( $_[0] ) eq 'HASH' )
113                 || Carp::confess "Single parameters to new() must be a HASH ref";
114                 +{ %{ $_[0] } };
115             }
116             else {
117                 +{};
118             }
119         }
120         else {
121             +{@_};
122         }
123     };
124 ...
125 }
126
127 sub _generate_BUILDALL {
128     my ($class, $meta) = @_;
129     return '' unless $meta->name->can('BUILD');
130
131     my @code = ();
132     push @code, q{no strict 'refs';};
133     push @code, q{no warnings 'once';};
134     no strict 'refs';
135     for my $class ($meta->linearized_isa) {
136         if (*{ $class . '::BUILD' }{CODE}) {
137             push  @code, qq{${class}::BUILD->(\$instance, \$args);};
138         }
139     }
140     return join "\n", @code;
141 }
142
143 1;