Perl 的 die() 方法调用神秘地没有结束

8

在工作中,我经过严格的调试才发现了项目中一个非常晦涩难懂的 bug,在这之后,我写出了下面这段简短的代码。其中一个 die 调用没有起到终止程序的作用。

问题只会在调用 script.pl 时出现。如果直接调用 Class_A,那么 die 调用将会成功。

我们需要三个文件:

文件1: script.pl

use strict;
use warnings;
use lib '.';
use Class_A;

# This should not execute. Class_A should die at loading time
print "We shouldn't get here. Class_A shoud not load and die.\n";

文件2:Class_A.pm

package Class_A;
use strict;
use warnings;
use Class_B;

# This code SHOULD die:
my $p = Class_B->new;
$p->do_something->die_now;


1;

文件 3:Class_B.pm

package Class_B;
use strict;
use warnings;

sub new {
    my $class = shift;
    bless {}, $class;
}

sub do_something {
    my $self = shift;
}

sub die_now {
    die "No soup for you!";
}

sub DESTROY {
    eval {
        1;
    };
}

1;

注意这个链接调用 at Class_A.pm line 8 吗?如果你将其断开,代码就可以成功运行了。:-|

# This works. There should be no difference.
$p->do_something;
$p->die_now;

最后的惊喜是发现仅仅通过删除 Class_B.pm line 19 处的 eval 调用,然后事情就如预期的那样工作并终止了脚本。

我有机会在 Perl 5.22.2Perl 5.26.1Perl 5.32.0 中测试了这个问题。令人惊讶的是,这个问题只在 5.32.0 中不存在。

真的,WT*?对此有何想法吗?


有趣!我不确定根本原因,但是 perldoc 上的 eval 页面有这样一句话:“它 [eval] 也是 Perl 的异常捕获机制,其中 die 运算符用于引发异常。” 看起来 Die+Destructor+Eval 的组合让人感到困惑……就像可能在 Die 中嵌套 eval 时出现了问题。 - Brad
你忘记将其与链式调用混合,因为如果不是链式调用,那么一切都能正常工作。 - Francisco Zarabozo
2
提示: . 不是脚本的目录。请使用 use FindBin qw( $RealBin ); use lib $RealBin; - ikegami
1个回答

10

您发布的代码在5.28版本以后没有表现出该问题。

问题与$@被破坏(使用eval { })与由于异常而发生的回退有关。显然,取消设置$@会让Perl认为没有发生异常。

根据perl528delta,现在在销毁所有内容之后才设置$@,这可以防止析构函数破坏$@。您还可以通过添加local $@;来防止析构函数破坏$@(例如,支持旧版本的Perl)。


1
有趣。你认为为什么链式调用会出现这种情况,而非链式调用则不会?Eval在同一时间仍然执行相同的操作。 - Francisco Zarabozo
2
与堆栈上的变量何时被释放有关。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接