Вызов Perl 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;

И последним сюрпризом было узнать, что просто удалив вызов eval at Class_B.pm line 19, все работает, как и ожидалось, и скрипт умирает.

У меня была возможность проверить это в Perl 5.22.2, Perl 5.26.1 и Perl 5.32.0. Еще один удивительный сюрприз: эта проблема возникает не только на 5.32.0.

Серьёзно, ВТ*? Любые мысли о том, что здесь происходит?


person Francisco Zarabozo    schedule 14.08.2020    source источник
comment
Интересный! Я не уверен в первопричине, но на странице perldoc в eval есть это небольшое утверждение: это [eval] также является механизмом перехвата исключений Perl, где оператор die используется для создания исключений. Кажется, комбинация Die+Destructor+ Eval путается.... как будто что-то не так с вложением eval из Die с eval в Destroy.   -  person Brad    schedule 14.08.2020
comment
Вы забываете смешивать его с цепным вызовом, потому что если не цепной, то все работает хорошо.   -  person Francisco Zarabozo    schedule 14.08.2020
comment
Совет: . не является каталогом сценария. Используйте use FindBin qw( $RealBin ); use lib $RealBin;.   -  person ikegami    schedule 14.08.2020


Ответы (1)


В опубликованном вами коде проблема не возникает с версии 5.28.

Проблема связана с тем, что $@ затирается (с использованием eval { }) во время очистки, которая происходит из-за исключения. По-видимому, отключение $@ обманывает Perl, заставляя его думать, что исключения не произошло.

Согласно perl528delta, $@ теперь устанавливается после того, как все будет уничтожено, что предотвращает стирание деструктором $@. Вы также можете запретить деструктору затирать $@, добавив local $@; (например, для поддержки старых версий Perl).

person ikegami    schedule 14.08.2020
comment
Интересный. Любые мысли о том, почему это происходит с цепным вызовом, а не с его развязкой? Eval все еще делает то же самое в то же самое время. - person Francisco Zarabozo; 14.08.2020
comment
Имеет отношение именно к тому, когда переменные в стеке освобождаются. - person ikegami; 14.08.2020