File Coverage

blib/lib/Return/MultiLevel.pm
Criterion Covered Total %
statement 21 21 100.0
branch 5 6 83.3
condition n/a
subroutine 7 7 100.0
pod n/a
total 33 34 97.0


line stmt bran cond sub pod time code
1             package Return::MultiLevel;
2              
3 3     3   211873 use strict;
  3         34  
  3         89  
4 3     3   16 use warnings;
  3         7  
  3         65  
5 3     3   62 use 5.008001;
  3         11  
6 3     3   19 use Carp qw(confess);
  3         5  
  3         186  
7 3     3   1404 use parent 'Exporter';
  3         943  
  3         18  
8              
9             # ABSTRACT: Return across multiple call levels
10             our $VERSION = '0.08'; # VERSION
11              
12             our @EXPORT_OK = qw(with_return);
13              
14             our $_backend;
15              
16             sub with_return (&);
17              
18             if (!$ENV{RETURN_MULTILEVEL_PP} && eval { require Scope::Upper }) {
19              
20             *with_return = sub (&) {
21 26     26   2931 my ($f) = @_;
22 26         59 my $ctx = Scope::Upper::HERE();
23             my @canary = !$ENV{RETURN_MULTILEVEL_DEBUG}
24 26 100       244 ? '-'
25             : Carp::longmess "Original call to with_return";
26              
27 26         143 local $canary[0];
28             $f->(sub {
29 15 50   15   366 $canary[0] and confess $canary[0] eq '-'
    100          
30             ? ""
31             : "Captured stack:\n$canary[0]\n",
32             "Attempt to re-enter dead call frame";
33 14         111 Scope::Upper::unwind(@_, $ctx);
34             })
35 26         85 };
36              
37             $_backend = 'XS';
38              
39             } else {
40              
41             *_label_at = do {
42             my $_label_prefix = '_' . __PACKAGE__ . '_';
43             $_label_prefix =~ tr/A-Za-z0-9_/_/cs;
44              
45             sub { $_label_prefix . $_[0] };
46             };
47              
48             our @_trampoline_cache;
49              
50             *_get_trampoline = sub {
51             my ($i) = @_;
52             my $label = _label_at($i);
53             (
54             $label,
55             $_trampoline_cache[$i] ||= eval ## no critic (BuiltinFunctions::ProhibitStringyEval)
56             qq{
57             sub {
58             my \$rr = shift;
59             my \$fn = shift;
60             return &\$fn;
61             $label: splice \@\$rr
62             }
63             },
64             )
65             };
66              
67             our $_depth = 0;
68              
69             *with_return = sub (&) {
70             my ($f) = @_;
71             my ($label, $trampoline) = _get_trampoline($_depth);
72             local $_depth = $_depth + 1;
73             my @canary = !$ENV{RETURN_MULTILEVEL_DEBUG}
74             ? '-'
75             : Carp::longmess "Original call to with_return";
76              
77             local $canary[0];
78             my @ret;
79             $trampoline->(
80             \@ret,
81             $f,
82             sub {
83             $canary[0] and confess $canary[0] eq '-'
84             ? ""
85             : "Captured stack:\n$canary[0]\n",
86             "Attempt to re-enter dead call frame";
87             @ret = @_;
88             goto $label;
89             },
90             )
91             };
92              
93             $_backend = 'PP';
94             }
95              
96             1;
97              
98             __END__