File Coverage

blib/lib/Scalar/Defer.pm
Criterion Covered Total %
statement 96 107 89.7
branch 15 30 50.0
condition 7 15 46.6
subroutine 26 27 96.3
pod 3 4 75.0
total 147 183 80.3


line stmt bran cond sub pod time code
1             package Scalar::Defer;
2              
3 2     2   49 use 5.006;
  2         6  
  2         74  
4 2     2   10 use strict;
  2         4  
  2         54  
5 2     2   10 use warnings;
  2         3  
  2         146  
6              
7             BEGIN {
8 2     2   5 our $VERSION = '0.23';
9 2         6 our @EXPORT = qw( lazy defer force );
10 2         50 our @EXPORT_OK = qw( is_deferred );
11             }
12              
13 2     2   11 use Scalar::Util;
  2         3  
  2         141  
14 2     2   1795 use Exporter::Lite;
  2         1488  
  2         18  
15 2     2   2313 use Class::InsideOut qw( register id );
  2         27157  
  2         15  
16 2     2   226 use constant DEFER_PACKAGE => '0'; # This may change soon
  2         5  
  2         585  
17              
18             BEGIN {
19 2     2   5 my %_defer;
20              
21             sub defer (&) {
22 4     4 1 42 my $cv = shift;
23 4         42 my $obj = register( bless(\(my $id) => __PACKAGE__) );
24 4         88 $_defer{ $id = id $obj } = $cv;
25 4         19 bless($obj => DEFER_PACKAGE);
26             }
27              
28             sub lazy (&) {
29 2     2 1 17 my $cv = shift;
30 2         11 my $obj = register( bless(\(my $id) => __PACKAGE__) );
31              
32 2         24 my ($value, $forced);
33             $_defer{ $id = id $obj } = sub {
34 7 100   7   53 $forced ? $value : scalar(++$forced, $value = &$cv)
35 2         110 };
36              
37 2         10 bless $obj => DEFER_PACKAGE;
38             }
39              
40             sub DEMOLISH {
41 6     6 0 121 delete $_defer{ id $_[0] };
42             }
43              
44             sub is_deferred ($) {
45 2     2   11 no warnings 'uninitialized';
  2         2  
  2         161  
46 21     21 1 125 ref $_[0] eq DEFER_PACKAGE;
47             }
48              
49             use constant SUB_FORCE => sub ($) {
50 2     2   9 no warnings 'uninitialized';
  2         4  
  2         289  
51             &{
52 31     31   396 $_defer{ id $_[0] } ||= $_defer{do {
53             #
54             # The memory address was dislocated. Fortunately, its original
55             # refaddr is saved directly inside the scalar referent slot.
56             #
57             # So we remove the overload by blessing into UNIVERSAL, get the
58             # original refaddr back, and register it with ||= above to avoid
59             # doing the same thing next time. (Afterwards we rebless it back.)
60             #
61             # This of course assumes that nobody overloads ${} for UNIVERSAL
62             # (which will naturally break all objects using scalar-ref layout);
63             # if someone does, that someone is more crazy than we are and should
64             # be able to handle the consequences.
65             #
66 7         10 my $self = $_[0];
67 7 100       26 ref($self) eq DEFER_PACKAGE or return $self;
68              
69 6         12 bless($self => 'UNIVERSAL');
70 6         10 my $id = $$self;
71 6         13 bless($self => DEFER_PACKAGE);
72 6         33 $id;
73 31 100 100     195 }} or do {
74 6 50       33 return 0 if caller eq 'Class::InsideOut';
75 0         0 die sprintf("Cannot locate thunk for memory address: 0x%X", id $_[0]);
76             };
77             };
78 2     2   10 };
  2         3  
  2         105  
79              
80 2         136 *force = SUB_FORCE();
81             }
82              
83             BEGIN {
84             package Scalar::Defer::Deferred;
85             use overload (
86 16         39 fallback => 1, map {
87 2         8 $_ => Scalar::Defer::SUB_FORCE(),
88             } qw( bool "" 0+ ${} @{} %{} &{} *{} )
89 2     2   3257 );
  2         2635  
90              
91             sub AUTOLOAD {
92 1     1   28 my $meth = our $AUTOLOAD;
93 1         5 my $idx = index($meth, '::');
94              
95 1 50       6 if ($idx >= 0) {
96 1         5 $meth = substr($meth, $idx + 2);
97             }
98              
99 1         6 unshift @_, Scalar::Defer::SUB_FORCE()->(shift(@_));
100 1         8 goto &{$_[0]->can($meth)};
  1         10  
101             };
102              
103             {
104 2 50 33 2   874 foreach my $sym (grep {
  2   33     8  
  8   33     145  
      33        
105             $_ ne 'DESTROY' and $_ ne 'DEMOLISH' and $_ ne 'BEGIN'
106             and $_ ne 'END' and $_ ne 'AUTOLOAD' and $_ ne 'CLONE_SKIP'
107             } keys %UNIVERSAL::) {
108 8         16 my $code = q[
109             sub $sym {
110             if ( defined Scalar::Util::blessed($_[0]) ) {
111             unshift @_, Scalar::Defer::SUB_FORCE()->(shift(@_));
112             goto &{$_[0]->can("$sym")};
113             }
114             else {
115             # Protect against future ALLCAPS methods
116             return if $_[0] eq Scalar::Defer::DEFER_PACKAGE;
117              
118             return shift->SUPER::$sym(@_);
119             }
120             }
121             ];
122              
123 8         47 $code =~ s/\$sym/$sym/g;
124              
125 8         13 local $@;
126 8 0   0   1014 eval $code;
  0 0   1   0  
  0 0   5   0  
  0 50   1   0  
  0 50       0  
  0 100       0  
  0 0       0  
  1 50       8  
  1         5  
  1         47  
  1         22  
  0         0  
  0         0  
  5         58  
  4         14  
  4         118  
  4         40  
  1         5  
  1         15  
  1         8  
  1         19  
  1         31  
  1         16  
  0            
  0            
127 8 50       31 warn $@ if $@;
128             }
129              
130 2         8 *DESTROY = \&Scalar::Defer::DESTROY;
131 2         45 *DEMOLISH = \&Scalar::Defer::DEMOLISH;
132             }
133             }
134              
135             BEGIN {
136 2     2   20 no strict 'refs';
  2         4  
  2         75  
137 2     2   5 @{DEFER_PACKAGE().'::ISA'} = ('Scalar::Defer::Deferred');
  2         92  
138             }
139              
140             1;
141              
142             __END__