/
Code.pm
105 lines (80 loc) · 1.9 KB
/
Code.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/perl
package Data::Thunk::Code;
use strict;
use warnings;
use Data::Swap;
use Scalar::Util qw(reftype blessed);
use Carp;
use namespace::clean;
use UNIVERSAL::ref;
BEGIN {
our $vivify_code = sub {
bless $_[0], "Data::Thunk::NoOverload";
my $scalar = reftype($_[0]) eq "REF";
my $code = $scalar ? ${ $_[0] } : $_[0]->{code};
my $tmp = $_[0]->$code();
if ( CORE::ref($tmp) ) {
my ( $ret, $e ) = do {
local $@;
eval { swap $_[0], $tmp; 1 }, $@;
};
unless ( $ret ) {
# try to figure out where the thunk was defined
my $lazy_ctx = eval {
require B;
my $cv = B::svref_2object($_[0]->{code});
my $file = $cv->FILE;
my $line = $cv->START->line;
"in thunk defined at $file line $line";
} || "at <<unknown>>";
my $file = quotemeta(__FILE__);
$e =~ s/ at $file line \d+.\n$/ $lazy_ctx, vivified/; # becomes "vivified at foo line blah"..
croak($e);
}
return $_[0];
} else {
if ( $scalar ) {
${ $_[0] } = $tmp;
} else {
Data::Swap::swap $_[0], do { my $o = $tmp; \$o };
}
bless $_[0], "Data::Thunk::ScalarValue";
return $_[0];
}
};
}
our $vivify_code;
use overload ( fallback => 1, map { $_ => $vivify_code } qw( bool "" 0+ ${} @{} %{} &{} *{} ) );
our $vivify_and_call = sub {
my $method = shift;
$_[0]->$vivify_code();
goto &{$_[0]->can($method)}
};
sub ref {
CORE::ref($_[0]->$vivify_code);
}
foreach my $sym (keys %UNIVERSAL::) {
no strict 'refs';
next if $sym eq 'ref::';
next if defined &$sym;
local $@;
*{$sym} = eval "sub {
if ( Scalar::Util::blessed(\$_[0]) ) {
unshift \@_, '$sym';
goto \$vivify_and_call;
} else {
shift->SUPER::$sym(\@_);
}
}" || die $@;
}
sub AUTOLOAD {
my ( $self, @args ) = @_;
my ( $method ) = ( our $AUTOLOAD =~ /([^:]+)$/ );
unshift @_, $method;
goto $vivify_and_call;
}
sub DESTROY {
# don't create the value just to destroy it
}
__PACKAGE__
__END__