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