File Coverage

blib/lib/ImplicitThis.pm
Criterion Covered Total %
statement 21 55 38.1
branch 0 10 0.0
condition 0 2 0.0
subroutine 7 10 70.0
pod 0 1 0.0
total 28 78 35.9


line stmt bran cond sub pod time code
1             package ImplicitThis;
2              
3 1     1   22081 use 5.6.0;
  1         4  
  1         41  
4 1     1   6 use strict;
  1         2  
  1         73  
5              
6             our $VERSION = '0.01_001';
7             $VERSION = eval $VERSION; # see L
8              
9             # ImplicitThis -
10             # Modify a package to implicitly take "this" as a first argument, and to access
11             # object fields by default.
12              
13             # to use, in your code, do:
14              
15             # use ImplicitThis; ImplicitThis::imply();
16              
17             # does anyone know how to hand the CPU back after we finish loading, but take it
18             # again when whomever used us finishes loading?
19              
20             sub import {
21 1     1   9 my $callerpackage = caller;
22 1     1   16 no strict 'refs';
  1         8  
  1         113  
23            
24 1         14 *{$callerpackage.'::caller'} = sub {
25 0   0 0     my $lev = shift() || 0;
26             # account for the ::imply() generated wrapper, and account for this function here
27 0           $lev += 2;
28 0           return CORE::caller($lev);
29 1         4 };
30              
31             }
32              
33             sub imply {
34              
35 0     0 0   my $callerpackage = caller;
36              
37 1     1   5 no strict 'refs';
  1         1  
  1         25  
38 1     1   4 no strict 'vars';
  1         2  
  1         34  
39 1     1   4 no strict 'subs';
  1         1  
  1         375  
40              
41             # loop over symbols in their name table, finding things that are functions
42              
43 0           for $i (grep { defined &{$callerpackage.'::'.$_} } keys %{$callerpackage.'::'}) {
  0            
  0            
  0            
44              
45 0 0         next if $i eq 'new';
46 0 0         next if $i eq 'caller';
47              
48 0           my $funname = $i;
49 0           my $funfun = \&{$callerpackage.'::'.$i}; # reference to pre-modified code
  0            
50              
51             # make a new code reference. give it the same name. lexically bind it to the old reference.
52              
53 0           *{$callerpackage.'::'.$funname} = sub {
54 0     0     my $this = shift;
55 0           my $newcp = caller;
56              
57 0 0         if(substr($funname, 0, 1) eq '_') {
58             # we're supposed to be private. see to the fact.
59 0 0         caller(0) eq $callerpackage or
60             die sprintf "Cannot invoke private method %s from outside %s",
61             $funname, __PACKAGE__;
62             }
63            
64 0           @ISA = @{$newcp.'::ISA'}; # become one of whatever called us
  0            
65              
66             # create a $this local that contains the pointer to the object the method
67             # was called in. this lets people say things like: $this->get_foo();
68             # XXX to emulate "normal" instance variable access, we could AUTOLOAD and
69             # XXX treat $this->var as a method call, translating it to $this->{'var'}
70              
71 0           local *{$callerpackage.'::this'};
  0            
72 0           *{$callerpackage.'::this'} = \$this;
  0            
73              
74             # give the same treatment to each instance variable: localize it for safety,
75             # then make it an alias to the hash entry that contains the instance variable.
76              
77 0           my @fields = keys %$this;
78 0           my $field;
79              
80 0           FIELDS:
81              
82             $field = shift @fields;
83              
84 0           local *{$callerpackage.'::'.$field};
  0            
85 0           *{$callerpackage.'::'.$field} = \$this->{$field};
  0            
86            
87 0 0         goto FIELDS if(@fields);
88              
89             # goto &$funfun; # this wont work because it immediately restores all variables we localized
90             # we just have to deal with a bogus stack frame lingering
91              
92             # invoke the code reference that we've secret replaced ourselves with.
93              
94 0           $funfun->(@_);
95              
96             # *now* local variables get restored
97              
98             }
99 0           }
100             }
101              
102             1;
103              
104             1;
105             __END__