File Coverage

blib/lib/CatalystX/ASP/Compiler.pm
Criterion Covered Total %
statement 74 74 100.0
branch 12 22 54.5
condition 3 6 50.0
subroutine 18 18 100.0
pod n/a
total 107 120 89.1


line stmt bran cond sub pod time code
1             package CatalystX::ASP::Compiler;
2              
3 13     13   5924 use Moose::Role;
  13         2892  
  13         257  
4              
5 13     13   32363 use File::Slurp qw(read_file);
  13         18585  
  13         538  
6 13     13   49 use Carp;
  13         19  
  13         2858  
7              
8             with 'CatalystX::ASP::Parser';
9              
10             requires 'parse_file';
11              
12             =head1 NAME
13              
14             CatalystX::ASP::Compiler - Role for CatalystX::ASP providing code compilation
15              
16             =head1 SYNOPSIS
17              
18             use CatalystX::ASP;
19             with 'CatalystX::ASP::Compiler';
20              
21             sub execute {
22             my ($self, $c, $scriptref) = @_;
23             my $parsed = $self->parse($c, $scriptref);
24             my $subid = $self->compile($c, $parsed->{data});
25             eval { &$subid };
26             }
27              
28             =head1 DESCRIPTION
29              
30             This class implements the ability to compile parsed ASP code.
31              
32             =cut
33              
34             has '_compiled_includes' => (
35             is => 'rw',
36             isa => 'HashRef',
37             default => sub { {} },
38             traits => [qw(Hash)],
39             handles => {
40             _get_compiled_include => 'get',
41             _add_compiled_include => 'set',
42             _include_is_compiled => 'exists',
43             },
44             );
45              
46             has '_registered_includes' => (
47             is => 'rw',
48             isa => 'HashRef',
49             default => sub { {} },
50             traits => [qw(Hash)],
51             handles => {
52             _include_is_registered => 'exists',
53             _add_registered_include => 'set',
54             },
55             );
56              
57             =head1 METHODS
58              
59             =over
60              
61             =item $self->compile($c, $scriptref, $subid)
62              
63             Takes a C<$scriptref> that has been parsed and C<$subid> for the name of the
64             subroutine to compile the code into. Returns
65              
66             =cut
67              
68             sub compile {
69 18     18   61 my ( $self, $c, $scriptref, $subid ) = @_;
70              
71 18         284 my $package = $self->GlobalASA->package;
72 18         167 $self->_undefine_sub( $subid );
73              
74 16         101 my $code = join( ' ;; ',
75             "package $package;", # for no sub closure
76             "no strict;",
77             "sub $subid { ",
78             "package $package;", # for sub closure
79             $$scriptref,
80             '}',
81             );
82 16         46 $code =~ /^(.*)$/s; # Realized this is for untainting
83 16         55 $code = $1;
84              
85 12     12   361 no warnings;
  12         276958  
  12         4208  
86 16 100       261 local $SIG{__DIE__} = \&Carp::confess if $self->Debug;
87 16     10   1025 eval $code; ## no critic (BuiltinFunctions::ProhibitStringyEval)
  7     8   41  
  7     7   12  
  6     2   179  
  6     1   22  
  5     1   5  
  5     1   113  
  5     1   17  
  5         5  
  5         417  
88 16 50       106 if ( $@ ) {
89 1         4 $c->error( "Error on compilation of $subid: $@" ); # don't throw error, so we can throw die later
90 1         1 $self->_undefine_sub( $subid );
91 1         168 return;
92             } else {
93 16         58 $self->register_include( $c, $scriptref );
94 16         80 return $subid;
95             }
96             }
97              
98             =item $self->compile_include($c, $include)
99              
100             Takes an C<$include> file. This will search for the file in C<IncludesDir> and
101             parse it, and assign it a C<$subid> based on it's filename.
102              
103             =cut
104              
105             sub compile_include {
106 14     14   449 my ( $self, $c, $include ) = @_;
107              
108 11         46 my $file = $self->search_includes_dir( $include );
109 13 50       674 unless ( $file ) {
110 1         22 $c->error( "Error in compilation: $include not found" );
111 3         8 return;
112             }
113              
114 13         37 return $self->compile_file( $c, $file );
115             }
116              
117             =item $self->compile_file($c, $file)
118              
119             Takes an C<$file> assuming it exists. This will search for the file in
120             C<IncludesDir> and parse it, and assign it a C<$subid> based on it's filename.
121              
122             =cut
123              
124             sub compile_file {
125 23     23   2589 my ( $self, $c, $file ) = @_;
126              
127 23         93 my $id = $self->file_id( $file );
128 23         496 my $subid = join( '', $self->GlobalASA->package, '::', $id, 'xINC' );
129              
130 23 100       755 return $self->_get_compiled_include( $subid ) if $self->_include_is_compiled( $subid );
131              
132 16         81 my $parsed_object = $self->parse_file( $c, $file );
133 17 50       40 return unless $parsed_object;
134              
135             my %compiled_object = (
136             mtime => time(),
137             perl => $parsed_object->{data},
138 17         110 file => $file,
139             );
140              
141 17 100 66     711 if ( $parsed_object->{is_perl}
    50          
142             && ( my $code = $self->compile( $c, $parsed_object->{data}, $subid ) ) ) {
143 14         28 $compiled_object{is_perl} = 1;
144 14         110 $compiled_object{code} = $code;
145             } elsif ( $parsed_object->{is_raw} ) {
146 3         16 $compiled_object{is_raw} = 1;
147 3         10 $compiled_object{code} = $parsed_object->{data};
148             } else {
149 2         46 return;
150             }
151              
152             # for a returned code ref, don't cache
153             $self->_add_compiled_include( $subid => \%compiled_object )
154 15 50 33     90 if ( $subid && !$self->_parse_for_subs( $parsed_object->{data} ) );
155              
156 15         60 return \%compiled_object;
157             }
158              
159             =item $self->register_include($c, $scriptref)
160              
161             Registers the file file of any calls to C<< $Response->Include() >> so as to
162             prevent infinite recursion
163              
164             =cut
165              
166             sub register_include {
167 17     18   52 my ( $self, $c, $scriptref ) = @_;
168              
169 17         34 my $copy = $$scriptref;
170 17         85 $copy =~ s/\$Response\-\>Include\([\'\"]([^\$]+?)[\'\"]/
171             {
172 2         96 my $include = $1;
  1         4  
173             # prevent recursion
174 1 0       6 unless( $self->_include_is_registered( $include ) ) {
175 1         25 $self->_add_registered_include( $include => 1 );
176 1         2 eval { $self->compile_include( $c, $include ); };
  1         1  
177 1 0       4 $c->log->warn( "Register include $include with error: $@" ) if $@;
178             }
179 1         4 '';
180             } /exsgi;
181             }
182              
183             # This is how CHAMAS gets a subroutined destroyed
184             sub _undefine_sub {
185 19     21   42 my ( $self, $subid ) = @_;
186 19 50       21 if ( my $code = \&{$subid} ) {
  18         180  
187 19         48 undef( &$code );
188             }
189             }
190              
191 12     12   54 no Moose::Role;
  12         15  
  12         252  
192              
193             1;
194              
195             =back
196              
197             =head1 SEE ALSO
198              
199             =over
200              
201             =item * L<CatalystX::ASP>
202              
203             =item * L<CatalystX::ASP::Parser>
204              
205             =back