I need the ability to add actions to the end of a lexical block where the action might die. And I need the exception to be thrown normally and be able to be caught normally.
Unfortunately, Perl special cases exceptions during DESTROY both by adding "(in cleanup)" to the message and making them untrappable. For example:
{
package Guard;
use strict;
use warnings;
sub new {
my $class = shift;
my $code = shift;
return bless $code, $class;
}
sub DESTROY {
my $self = shift;
$self->();
}
}
use Test::More tests => 2;
my $guard_triggered = 0;
ok !eval {
my $guard = Guard->new(
#line 24
sub {
$guard_triggered++;
die "En guarde!"
}
);
1;
}, "the guard died";
is $@, "En guarde! at $@ line 24\n", "with the right error message";
is $guard_triggered, 1, "the guard worked";
I want that to pass. Currently the exception is totally swallowed by the eval.
This is for Test::Builder2, so I cannot use anything but pure Perl.
The underlying issue is I have code like this:
{
$self->setup;
$user_code->();
$self->cleanup;
}
That cleanup must happen even if the $user_code dies, else $self gets into a weird state. So I did this:
{
$self->setup;
my $guard = Guard->new(sub { $self->cleanup });
$user_code->();
}
The complexity comes because the cleanup runs arbitrary user code and it is a use case where that code will die. I expect that exception to be trappable and unaltered by the guard.
I'm avoiding wrapping everything in eval blocks because of the way that alters the stack.