delete packages when we're done with them, to avoid leaking
[gitmo/Eval-Closure.git] / lib / Eval / Closure.pm
CommitLineData
efb592ef 1package Eval::Closure;
b3bd5eb8 2use strict;
3use warnings;
efb592ef 4use Sub::Exporter -setup => {
5 exports => [qw(eval_closure)],
ce19c70b 6 groups => { default => [qw(eval_closure)] },
efb592ef 7};
ed9a00ae 8# ABSTRACT: safely and cleanly create closures via string eval
efb592ef 9
10use Carp;
11use overload ();
12use Scalar::Util qw(reftype);
13use Try::Tiny;
14
ed9a00ae 15=head1 SYNOPSIS
16
2e6086ab 17 use Eval::Closure;
18
19 my $code = eval_closure(
20 source => 'sub { $foo++ }',
21 environment => {
22 '$foo' => \1,
23 },
24 );
25
26 warn $code->(); # 1
27 warn $code->(); # 2
28
29 my $code2 = eval_closure(
30 source => 'sub { $code->() }',
31 ); # dies, $code isn't in scope
32
ed9a00ae 33=head1 DESCRIPTION
34
2e6086ab 35String eval is often used for dynamic code generation. For instance, C<Moose>
36uses it heavily, to generate inlined versions of accessors and constructors,
37which speeds code up at runtime by a significant amount. String eval is not
38without its issues however - it's difficult to control the scope it's used in
9b61f781 39(which determines which variables are in scope inside the eval), and it's easy
40to miss compilation errors, since eval catches them and sticks them in $@
41instead.
2e6086ab 42
9b61f781 43This module attempts to solve these problems. It provides an C<eval_closure>
fa287851 44function, which evals a string in a clean environment, other than a fixed list
9b61f781 45of specified variables. Compilation errors are rethrown automatically.
2e6086ab 46
ed9a00ae 47=cut
48
49=func eval_closure(%args)
50
2e6086ab 51This function provides the main functionality of this module. It is exported by
52default. It takes a hash of parameters, with these keys being valid:
53
54=over 4
55
56=item source
57
58The string to be evaled. It should end by returning a code reference. It can
59access any variable declared in the C<environment> parameter (and only those
60variables). It can be either a string, or an arrayref of lines (which will be
61joined with newlines to produce the string).
62
63=item environment
64
65The environment to provide to the eval. This should be a hashref, mapping
66variable names (including sigils) to references of the appropriate type. For
67instance, a valid value for environment would be C<< { '@foo' => [] } >> (which
68would allow the generated function to use an array named C<@foo>). Generally,
69this is used to allow the generated function to access externally defined
70variables (so you would pass in a reference to a variable that already exists).
71
72=item description
73
74This lets you provide a bit more information in backtraces. Normally, when a
75function that was generated through string eval is called, that stack frame
76will show up as "(eval n)", where 'n' is a sequential identifier for every
77string eval that has happened so far in the program. Passing a C<description>
78parameter lets you override that to something more useful (for instance,
79L<Moose> overrides the description for accessors to something like "accessor
c4318911 80foo at MyClass.pm, line 123").
2e6086ab 81
75e6988b 82=item line
83
84This lets you override the particular line number that appears in backtraces,
c8d4a65f 85much like the C<description> option. The default is 1.
75e6988b 86
5617e966 87=item terse_error
88
89Normally, this function appends the source code that failed to compile, and
90prepends some explanatory text. Setting this option to true suppresses that
91behavior so you get only the compilation error that Perl actually reported.
92
2e6086ab 93=back
94
ed9a00ae 95=cut
96
efb592ef 97sub eval_closure {
98 my (%args) = @_;
8e1b3d7b 99
efb592ef 100 $args{source} = _canonicalize_source($args{source});
8e1b3d7b 101 _validate_env($args{environment} ||= {});
efb592ef 102
21665b5f 103 my $should_set_description = defined $args{description} && !($^P & 0x10);
104
c8d4a65f 105 $args{source} = _line_directive(@args{qw(line description)})
106 . $args{source}
21665b5f 107 if $should_set_description;
108
109 my $existed_before;
110 $existed_before = exists $::{"_<$args{description}"}
111 if $should_set_description;
3efcc087 112
409b8f41 113 my ($code, $e) = _clean_eval_closure(@args{qw(source environment)});
efb592ef 114
21665b5f 115 if (!$existed_before && $should_set_description) {
116 # this will be meaningless, and just leaks memory
117 delete $::{"_<$args{description}"};
118 }
119
5617e966 120 if (!$code) {
121 if ($args{terse_error}) {
122 die "$e\n";
123 }
124 else {
125 croak("Failed to compile source: $e\n\nsource:\n$args{source}")
126 }
127 }
efb592ef 128
129 return $code;
130}
131
132sub _canonicalize_source {
133 my ($source) = @_;
134
135 if (defined($source)) {
136 if (ref($source)) {
137 if (reftype($source) eq 'ARRAY'
138 || overload::Method($source, '@{}')) {
139 return join "\n", @$source;
140 }
141 elsif (overload::Method($source, '""')) {
142 return "$source";
143 }
144 else {
145 croak("The 'source' parameter to eval_closure must be a "
146 . "string or array reference");
147 }
148 }
149 else {
150 return $source;
151 }
152 }
153 else {
154 croak("The 'source' parameter to eval_closure is required");
155 }
156}
157
8e1b3d7b 158sub _validate_env {
159 my ($env) = @_;
160
161 croak("The 'environment' parameter must be a hashref")
162 unless reftype($env) eq 'HASH';
163
164 for my $var (keys %$env) {
b3bd5eb8 165 croak("Environment key '$var' should start with \@, \%, or \$")
8e1b3d7b 166 unless $var =~ /^([\@\%\$])/;
167 croak("Environment values must be references, not $env->{$var}")
168 unless ref($env->{$var});
169 }
170}
171
3efcc087 172sub _line_directive {
75e6988b 173 my ($line, $description) = @_;
174
c8d4a65f 175 $line = 1 unless defined($line);
3efcc087 176
75e6988b 177 return qq{#line $line "$description"\n};
3efcc087 178}
179
efb592ef 180sub _clean_eval_closure {
1a2acf75 181 my ($source, $captures) = @_;
efb592ef 182
25ef0135 183 my @capture_keys = sort keys %$captures;
184
a30f41f7 185 if ($ENV{EVAL_CLOSURE_PRINT_SOURCE}) {
25ef0135 186 _dump_source(_make_compiler_source($source, @capture_keys));
a30f41f7 187 }
efb592ef 188
447800b5 189 my ($compiler, $e) = _make_compiler($source, @capture_keys);
f3c27658 190 my $code;
191 if (defined $compiler) {
447800b5 192 $code = $compiler->(@$captures{@capture_keys});
f3c27658 193 }
26eb0e7a 194
b86710e9 195 if (defined($code) && (!ref($code) || ref($code) ne 'CODE')) {
3eb05ecb 196 $e = "The 'source' parameter must return a subroutine reference, "
197 . "not $code";
26eb0e7a 198 undef $code;
26eb0e7a 199 }
200
18b5b42a 201 return ($code, $e);
efb592ef 202}
203
fa287851 204sub _make_compiler {
71625306 205 my $package = _next_package();
206 my $source = _make_compiler_source($package, @_);
207 my @ret = @{ _clean_eval($source) };
208 {
209 my ($first_fragments, $last_fragment) = ($package =~ /^(.*)::(.*)$/);
210
211 no strict 'refs';
212 # clear @ISA first, to avoid a memory leak
213 # see https://rt.perl.org/rt3/Public/Bug/Display.html?id=92708
214 @{$package . '::ISA'} = ();
215 %{$package . '::'} = ();
216 delete ${$first_fragments . '::'}{$last_fragment . '::'};
217 }
218 return @ret;
f3c27658 219}
220
0fb2ea46 221sub _clean_eval {
01a39ce3 222 local $@;
223 local $SIG{__DIE__};
224 my $compiler = eval $_[0];
225 my $e = $@;
226 [ $compiler, $e ];
0fb2ea46 227}
228
71625306 229{
230 $Eval::Closure::SANDBOX_ID = 0;
231
232 sub _next_package {
233 $Eval::Closure::SANDBOX_ID++;
234 return "Eval::Closure::Sandbox_$Eval::Closure::SANDBOX_ID";
235 }
236}
e6c246fb 237
f3c27658 238sub _make_compiler_source {
71625306 239 my ($package, $source, @capture_keys) = @_;
f3c27658 240 my $i = 0;
efb592ef 241 return join "\n", (
71625306 242 "package $package;",
f3c27658 243 'sub {',
efb592ef 244 (map {
f3c27658 245 'my ' . $_ . ' = ' . substr($_, 0, 1) . '{$_[' . $i++ . ']};'
447800b5 246 } @capture_keys),
efb592ef 247 $source,
f3c27658 248 '}',
efb592ef 249 );
250}
251
252sub _dump_source {
409b8f41 253 my ($source) = @_;
efb592ef 254
255 my $output;
256 if (try { require Perl::Tidy }) {
257 Perl::Tidy::perltidy(
258 source => \$source,
259 destination => \$output,
9688c823 260 argv => [],
efb592ef 261 );
262 }
263 else {
264 $output = $source;
265 }
266
409b8f41 267 warn "$output\n";
efb592ef 268}
269
ed9a00ae 270=head1 BUGS
271
272No known bugs.
273
274Please report any bugs through RT: email
275C<bug-eval-closure at rt.cpan.org>, or browse to
276L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Eval-Closure>.
277
278=head1 SEE ALSO
279
280=over 4
281
282=item * L<Class::MOP::Method::Accessor>
283
284This module is a factoring out of code that used to live here
285
286=back
287
288=head1 SUPPORT
289
290You can find this documentation for this module with the perldoc command.
291
292 perldoc Eval::Closure
293
294You can also look for information at:
295
296=over 4
297
298=item * AnnoCPAN: Annotated CPAN documentation
299
300L<http://annocpan.org/dist/Eval-Closure>
301
302=item * CPAN Ratings
303
304L<http://cpanratings.perl.org/d/Eval-Closure>
305
306=item * RT: CPAN's request tracker
307
308L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Eval-Closure>
309
310=item * Search CPAN
311
312L<http://search.cpan.org/dist/Eval-Closure>
313
314=back
315
316=head1 AUTHOR
317
318Jesse Luehrs <doy at tozt dot net>
319
320Based on code from L<Class::MOP::Method::Accessor>, by Stevan Little and the
321Moose Cabal.
322
323=cut
324
efb592ef 3251;