为什么`$ @`不值得信任?

时间:2010-09-10 17:36:16

标签: perl exception eval race-condition

我似乎记得在$@之后信任eval的价值是不安全的。关于信号处理程序有可能在你看到它之前设置$@的东西。我现在也太累了,懒得追查真正的原因。那么,为什么信任$@

是不安全的

3 个答案:

答案 0 :(得分:17)

Try::Tiny perldoc对$@的问题进行了明确的讨论:

  

eval存在许多问题。

     

Clobbering $ @

     

当您运行eval块并且成功时,$ @将被清除,可能会破坏当前捕获的错误。

     

这会导致远距离操作,清除调用者可能尚未处理的错误。

     

$ @必须在调用eval之前正确本地化,以避免此问题。

     

更具体地说,$ @在eval的开头被破坏了,这也使你无法在死之前捕获先前的错误(例如在制作具有错误堆栈的异常对象时)。

     

因此,尝试实际上将$ @设置为eval块开头的前一个值(在本地化之前)。

     

本地化$ @静默掩盖错误

     

在eval块中,die的行为类似于:

sub die {
        $@ = $_[0];
        return_undef_from_eval();
}
     

这意味着如果你礼貌和本地化的$ @你不能死在那个范围内,或者你的错误将被丢弃(打印出“Something's wrong”)。

     

解决方法非常难看:

my $error = do {
        local $@;
        eval { ... };
        $@;
};

...
die $error;
     

$ @可能不是真正的值

     

此代码错误:

if ( $@ ) {
        ...
}
     

因为之前的警告可能没有设置。

     

$ @也可能是一个评估为false的重载错误对象,但无论如何都要求麻烦。

     

经典的失败模式是:

sub Object::DESTROY {
        eval { ... }
}

eval {
        my $obj = Object->new;

        die "foo";
};

if ( $@ ) {

}
     

在这种情况下,由于Object :: DESTROY没有本地化$ @但仍然使用eval,它会将$ @设置为“”。

     

在堆栈展开后调用析构函数,在模具将$ @设置为“Foo.pm第42行的foo”之后,所以当if($ @)被计算时,它已被eval清除了析构函数。

     

这方面的解决方案甚至比以前的方法更加丑陋。即使我们无法从未本地化的代码中保存$ @的值,我们至少可以确保eval因错误而中止:

my $failed = not eval {
        ...

        return 1;
};
     

这是因为捕获骰子的eval将始终返回错误值。

答案 1 :(得分:13)

Try::Tiny docs有一个很好的eval / $@个缺点列表。我想你可能会引用那里的Localizing $@ silently masks errors部分。

答案 2 :(得分:7)

$@具有与每个全局变量相同的问题:当其他东西设置它时,它会在整个程序中重置。任何eval都可能设置$@。即使您没有在附近看到eval,也不知道还有谁可以调用它(子程序,绑定变量等)。