in reply to The safety of string eval and block eval.
Sometimes, you actually need to evaluate strings hard coded in the program. As an example, let's start with a simple iterator in the spirit of Dominus's Higher Order Perl:
This creates two closures which iterate on even or odd numbers.sub create_iterator{ my $val = shift; return sub { return $val += 2;} } my $iter_even = create_iterator(0); my $iter_odd = create_iterator(1);
Suppose now that you want to create a generic iterator in which you could pass an arbitrary function. If you try to do that, it might look like this:
and be called with a callback function like this:sub create_iter { my ($code_ref, @rest) = @_; return sub {$code_ref->();} }
That does not work because, since the $c variable is created within the callback subroutine passed to the create_iter subroutine, create_iter will not close over the variable which is defined elsewhere.my $even = create_iter(sub {my $c; $c += 2; return $c;}, 0); # Doe +s NOT work
Basically, this cannot work because the callback function is not defined within the environment that would make it a closure.
As far as I can tell, there is no solution with the techniques described above. So, we cannot make a generic iterator? Yes we can, if we use eval to create the callback function at the right place, using a string passed as a parameter. For example, the following code defines a generic iterator used to generate one by one Fibonacci numbers:
which will print:use strict; use warnings; sub create_iter { my ($code_string, @rest) = @_; eval $code_string; } my $fibonacci = create_iter ( ' return sub {my ($c, $d) = @rest; my $e = $c + $d; @rest = ($d, $e); return $e;} ', 1, 1); print "Fibonacci numbers: \n"; print $fibonacci->(), " ", for 1..7;
The point is that using eval makes it possible to create the closure code in the right place for the subroutine to close over the variable.Fibonacci: 2 3 5 8 13 21 34
I can now use the same generic iterator to generate factorials:
which will print:my $fact = create_iter (<<'EOS' return sub { $rest[0]++; $rest[1] *= $rest[0];}; EOS , 1, 1); print "\n\nFact: \n"; print $fact->(), " ", for 1..5;
The example might seem somewhat contrived, but that's the only way that I found to easily create a generic iterator when I needed one.Fact: 2 6 24 120 720
So, to me, string eval can in some cases be a very useful technique, and, as far as I can tell, there is no danger of malicious abuse, because the string used is actually hard-coded in the program.
Another example of useful string eval can be found in the Benchmark module, which offers the possibility to pass the function whose performance should be measured in the form of a string that is (I think) evaluated with eval.
In brief, my point is that string eval can be very useful and should not be ruled out, but, of course, can be dangerous if used, for example, with user input.
|
---|
Replies are listed 'Best First'. | |
---|---|
Re^2: The safety of string eval and block eval.
by choroba (Cardinal) on Aug 14, 2016 at 23:19 UTC | |
by Laurent_R (Canon) on Aug 15, 2016 at 11:13 UTC | |
by afoken (Chancellor) on Aug 15, 2016 at 19:51 UTC | |
by haukex (Archbishop) on Aug 16, 2016 at 07:45 UTC |