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   5499 use Moose::Role;
  13         30  
  13         305  
4              
5 13     13   46602 use File::Slurp qw(read_file);
  13         19570  
  13         678  
6 13     13   80 use Carp;
  13         30  
  13         2279  
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   121 my ( $self, $c, $scriptref, $subid ) = @_;
70              
71 18         300 my $package = $self->GlobalASA->package;
72 18         235 $self->_undefine_sub( $subid );
73              
74 16         131 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         69 $code =~ /^(.*)$/s; # Realized this is for untainting
83 16         84 $code = $1;
84              
85 12     12   393 no warnings;
  12         370478  
  12         4838  
86 16 100       301 local $SIG{__DIE__} = \&Carp::confess if $self->Debug;
87 16     8   1483 eval $code; ## no critic (BuiltinFunctions::ProhibitStringyEval)
  7     10   58  
  7     7   33  
  6     2   207  
  5     1   24  
  5     1   11  
  5     1   138  
  5     1   28  
  5         12  
  5         453  
88 16 50       135 if ( $@ ) {
89 1         5 $c->error( "Error on compilation of $subid: $@" ); # don't throw error, so we can throw die later
90 1         2 $self->_undefine_sub( $subid );
91 1         187 return;
92             } else {
93 16         90 $self->register_include( $c, $scriptref );
94 16         95 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   538 my ( $self, $c, $include ) = @_;
107              
108 12         91 my $file = $self->search_includes_dir( $include );
109 12 50       779 unless ( $file ) {
110 2         36 $c->error( "Error in compilation: $include not found" );
111 2         13 return;
112             }
113              
114 13         61 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 21     23   4726 my ( $self, $c, $file ) = @_;
126              
127 23         146 my $id = $self->file_id( $file );
128 23         544 my $subid = join( '', $self->GlobalASA->package, '::', $id, 'xINC' );
129              
130 23 100       846 return $self->_get_compiled_include( $subid ) if $self->_include_is_compiled( $subid );
131              
132 16         112 my $parsed_object = $self->parse_file( $c, $file );
133 16 50       82 return unless $parsed_object;
134              
135             my %compiled_object = (
136             mtime => time(),
137             perl => $parsed_object->{data},
138 16         118 file => $file,
139             );
140              
141 16 100 66     126 if ( $parsed_object->{is_perl}
    50          
142             && ( my $code = $self->compile( $c, $parsed_object->{data}, $subid ) ) ) {
143 14         53 $compiled_object{is_perl} = 1;
144 13         129 $compiled_object{code} = $code;
145             } elsif ( $parsed_object->{is_raw} ) {
146 5         38 $compiled_object{is_raw} = 1;
147 5         27 $compiled_object{code} = $parsed_object->{data};
148             } else {
149 4         1117 return;
150             }
151              
152             # for a returned code ref, don't cache
153             $self->_add_compiled_include( $subid => \%compiled_object )
154 15 50 33     114 if ( $subid && !$self->_parse_for_subs( $parsed_object->{data} ) );
155              
156 15         82 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   91 my ( $self, $c, $scriptref ) = @_;
168              
169 17         51 my $copy = $$scriptref;
170 17         92 $copy =~ s/\$Response\-\>Include\([\'\"]([^\$]+?)[\'\"]/
171             {
172 2         146 my $include = $1;
  1         6  
173             # prevent recursion
174 1 0       4 unless( $self->_include_is_registered( $include ) ) {
175 1         7 $self->_add_registered_include( $include => 1 );
176 1         18 eval { $self->compile_include( $c, $include ); };
  1         7  
177 1 0       5 $c->log->warn( "Register include $include with error: $@" ) if $@;
178             }
179 1         9 '';
180             } /exsgi;
181             }
182              
183             # This is how CHAMAS gets a subroutined destroyed
184             sub _undefine_sub {
185 19     21   88 my ( $self, $subid ) = @_;
186 19 50       45 if ( my $code = \&{$subid} ) {
  19         202  
187 19         96 undef( &$code );
188             }
189             }
190              
191 12     12   82 no Moose::Role;
  12         23  
  12         307  
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