File Coverage

blib/lib/Return/MultiLevel.pm
Criterion Covered Total %
statement 22 22 100.0
branch 5 6 83.3
condition n/a
subroutine 7 7 100.0
pod 1 1 100.0
total 35 36 97.2


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