protecting global variables during eval in Test::More

101 views Asked by At

I was scrolling through the code for Test::More because I wanted to see how use_ok and require_ok were implemented (and why Ovid doesn't like them). I came to the _eval subroutine, which contains some special logic that I don't fully understand for protecting variables during an eval call. I understand from the documentation of Try::Tiny how difficult it is to deal with $@. However, I don't understand the handling of $! and $SIG{__DIE__} (below block copied verbatim):

# Work around oddities surrounding resetting of $@ by immediately
# storing it.
my( $sigdie, $eval_result, $eval_error );
{
    local( $@, $!, $SIG{__DIE__} );    # isolate eval
    $eval_result = eval $code;              ## no critic (BuiltinFunctions::ProhibitStringyEval)
    $eval_error  = $@;
    $sigdie      = $SIG{__DIE__} || undef;
}
# make sure that $code got a chance to set $SIG{__DIE__}
$SIG{__DIE__} = $sigdie if defined $sigdie;

The handling of $SIG{__DIE__} is related to RT #34065, but I still don't get it. Why is it necessary to set the variable again in that last line, since it should always get set in the last line of the block? If the whole point of these lines is to set the variable to whatever it became in the eval call, why does it have to be localized in the first place?

Also, if using eval without clobbering error vars means localizing both $@ and $!, do we not also have to localize $??

1

There are 1 answers

2
ysth On BEST ANSWER

It looks to me like $sigdie is there to explictily allow the eval'd code to set $SIG{__DIE__}, while not allowing any externally set handler to take effect during the eval.

So the localization suppresses the external value, but requires saving any set value before the end of the block and setting it in the now not-localized version after the block.

I assume $! is localized but not $?, $^E, etc. for practical reasons (that is, I'd guess people complained about $! changing, but not anything else).