Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"no warnings;" in a Safe compartment

I am using reval from Perl's Safe module and I want to prevent it from generating warnings if the string being eval'ed can't be parsed (actually, I want to prevent it from generating any warnings at all).

For example, the following code:

use strict; use warnings;
use Safe;    
use feature qw/say/;
my $cft = Safe->new;

my $x = $cft->reval(') 1' );
my $y = $cft->reval('2'   );
say "x: $x";
say "y: $y";

results in:

Number found where operator expected at (eval 5) line 1, near ") 1"
    (Missing operator before 1?)
Use of uninitialized value $x in concatenation (.) or string at ./test line 12.
x: 
y: 2

What I'm trying to achieve is to have $x = undef and $y = 2, and no warnings. I tried to put a "no warnings;" inside a new scope, but it has no effect on the warnings produced from within the reval (although, as pointed out by @DavidO, it silences the 'uninitialized value' warning):

use strict; use warnings;
use Safe;    
use feature qw/say/;
my $cft = Safe->new;
{
    no warnings;
    my $x = $cft->reval(') 1' );
    my $y = $cft->reval('2'   );
    say "x: $x";
    say "y: $y";
}

I guess that somehow the 'no warnings' has to be inside the Safe compartment, so I also tried to prepend "no warnings;" to the strings being eval'ed:

use strict; use warnings;
use Safe;
use feature qw/say/;
my $cft = Safe->new;
{
    my $x = $cft->reval( 'no warnings;' . ') 1' );
    my $y = $cft->reval( 'no warnings;' . '2'   );
    say "x: $x";
    say "y: $y";
}

This way reval does not issue any warnings, but both variables are undef:

Use of uninitialized value $x in concatenation (.) or string at ./test line 10.
x: 
Use of uninitialized value $y in concatenation (.) or string at ./test line 11.
y:

I don't know what else to try, and I hope that the problem description was clear enough.

like image 770
andrefs Avatar asked Jul 26 '12 20:07

andrefs


2 Answers

If you check $@ you'll see that $cft->reval( 'no warnings;' . ') 1' ); failed. 'require' trapped by operation mask at (eval 5) line 1.. In other words, Safe is doing its job and preventing that code from trying to load a library.

$cft->reval( 'BEGIN { warnings->unimport; } ) 1' ); would work, presuming warnings is already loaded outside the compartment. However, that won't quiet compile time errors. Unlike eval, reval seems to let them through. Use amon's technique of quieting STDERR.

like image 117
Schwern Avatar answered Sep 28 '22 06:09

Schwern


no warnings suppresses all the warnings the use warnings pragma generates. You would probably want to remove any strictures as well. But severe parsing errors will pop up any way.

If you want to execute any code, no matter how pathological, without any output to STDERR, you should locally modify the signal handler:

{
  # I know what I'm doing!
  local $SIG{__WARN__} = sub {}; # locally ignore any warnings
  eval $code; # catches all "die"
}

or we could reopen STDERR to /dev/null:

{
  # I know what I'm doing!
  open my $oldSTDERR, '>&' \*STDERR or die;
  close STDERR or die;
  open STDERR, '>', '/dev/null' or die;

  eval $code;

  close STDERR or die;
  open STDERR, '>&', $oldSTDERR or die;
  close $oldSTDERR;
}
like image 22
amon Avatar answered Sep 28 '22 06:09

amon