Skip to content

Commit

Permalink
remove use of Return::MultiLevel, and implement stack frame jumping m…
Browse files Browse the repository at this point in the history
…anually

Return::MultiLevel obscures the fact that returning across multiple
stack frames is very evil.  Rather than hide the evil we are doing, just
embed the stack breaking via a label and last.

This avoids a Return::MultiLevel dependency, which avoids pulling in
Scope::Upper, another module with too much magic in it.  It also is
slightly faster, although that is a very minor benefit.
  • Loading branch information
haarg committed Nov 15, 2019
1 parent 868237a commit 36edc18
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 15 deletions.
1 change: 0 additions & 1 deletion cpanfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ requires 'Plack::Middleware::FixMissingBodyInRedirect';
requires 'Plack::Middleware::RemoveRedundantBody';
requires 'POSIX';
requires 'Ref::Util';
requires 'Return::MultiLevel';
requires 'Role::Tiny', '2.000000';
requires 'Safe::Isa';
requires 'Sub::Quote';
Expand Down
29 changes: 19 additions & 10 deletions lib/Dancer2/Core/App.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use Moo;
use Carp qw<croak carp>;
use Scalar::Util 'blessed';
use Module::Runtime 'is_module_name';
use Return::MultiLevel ();
use Safe::Isa;
use Sub::Quote;
use File::Spec;
Expand Down Expand Up @@ -1467,14 +1466,23 @@ DISPATCH:
}

# calling the actual route
my $response = Return::MultiLevel::with_return {
my ($return) = @_;

# stash the multilevel return coderef in the app
$self->has_with_return
or $self->set_with_return($return);

return $self->_dispatch_route($route);
my $response;

# this is very evil, but allows breaking out of multiple stack
# frames without throwing an exception. Avoiding exceptions means
# a naive eval won't swallow our flow control mechanisms, and
# avoids __DIE__ handlers. It also prevents some cleanup routines
# from working, since they are expecting control to return to them
# after an eval.
DANCER2_CORE_APP_ROUTE_RETURN: {
if (!$self->has_with_return) {
$self->set_with_return(sub {
$response = shift;
no warnings 'exiting';
last DANCER2_CORE_APP_ROUTE_RETURN;
});
}
$response = $self->_dispatch_route($route);
};

# ensure we clear the with_return handler
Expand Down Expand Up @@ -1679,7 +1687,8 @@ that package, thanks to that encapsulation.
=attr with_return
Used to cache the coderef from L<Return::MultiLevel> within the dispatcher.
Used to cache the coderef that will return from back to the dispatcher, across
an arbitrary number of stack frames.
=method has_session
Expand Down
7 changes: 3 additions & 4 deletions lib/Dancer2/Core/Dispatcher.pm
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ object and an env as input arguments.
C<dispatch> returns a response object of L<Dancer2::Core::Response>.
Any before hook and matched route code is wrapped using L<Return::MultiLevel>
to allow DSL keywords such as forward and redirect to short-circuit remaining code
without having to throw an exception. L<Return::MultiLevel> will use L<Scope::Upper>
(an XS module) if it is available.
Any before hook and matched route code is wrapped to allow DSL keywords such
as forward and redirect to short-circuit remaining code, returning across
multiple stack frames without having to throw an exception.
=head2 response_internal_error
Expand Down

0 comments on commit 36edc18

Please sign in to comment.