File Coverage

blib/lib/Object/Trampoline.pm
Criterion Covered Total %
statement 45 53 84.9
branch 9 14 64.2
condition n/a
subroutine 15 18 83.3
pod 0 1 0.0
total 69 86 80.2


line stmt bran cond sub pod time code
1             ########################################################################
2             # Object::Trampoline
3             # delay construction of objects until they are needed.
4             #
5             # the only purpose for the top two classes is having an autoload
6             # that blesses things into O::T::Bounce.
7             ########################################################################
8             ########################################################################
9             # housekeeping
10             ########################################################################
11              
12             package Object::Trampoline 1.42;
13 4     4   2891 use v5.12;
  4         8  
14              
15 4     4   15 use Carp;
  4         5  
  4         1054  
16              
17             ########################################################################
18             # package variables
19             ########################################################################
20              
21             # minimal sanity check for valid class name, used in both
22             # O::T and O::T::U.
23              
24             my $pkg_rx = qr{^ [_A-Za-z](?:\w*) (?:::\w+)* $}x;
25              
26             my $bounce_pkg = 'Object::Trampoline::Bounce';
27              
28             ########################################################################
29             # AUTOLOAD is the only public interface
30             ########################################################################
31             ########################################################################
32             #
33             # start by grabbing the destination class and its arguments
34             # off the stack. the constructor name is whatever is
35             # being autoloaded.
36             #
37             # there may not be any arguments... either way i need
38             # to make a lexical copy of the stack for use in the
39             # closure.
40             #
41             # the closure delays actual contstruction until it is
42             # dereferenced in O::T::Bounce::AUTOLOAD.
43             #
44             # $sub is syntatic sugar but is inexpensive enough to
45             # construct.
46             #
47             # Note: there are no DESTROY blocks in the constructing
48             # classes since no objects ever live there: they begin
49             # life in O::T::Bounce.
50              
51             our $AUTOLOAD = '';
52              
53             sub delayed
54             {
55 0     0 0 0 my ( $undef, $handler, @argz ) = @_;
56              
57 0     0   0 bless sub { $handler->( @argz ) }, $bounce_pkg
  0         0  
58             }
59              
60             AUTOLOAD
61             {
62             # discard this class: once here it is used up.
63             # lacking a prototype is fatal; anything else becomes
64             # a run-time error.
65              
66 10     10   1512 my ( undef, $proto, @argz ) = @_;
67              
68 10 100       23 $proto
69             or croak "Object::Trampoline: false prototype.";
70              
71 7         19 my $method = ( split /::/, "$AUTOLOAD" )[ -1 ];
72              
73 7     7   23 my $sub = sub { $proto->$method( @argz ) };
  7         25  
74              
75 7         19 bless $sub, $bounce_pkg
76             }
77              
78             ########################################################################
79             # same gizmo as O::T except that the class is use-ed before the
80             # constructor is called.
81             ########################################################################
82             # housekeeping
83             ########################################################################
84              
85             package Object::Trampoline::Use;
86 4     4   38 use v5.12;
  4         8  
87              
88 4     4   12 use Carp;
  4         4  
  4         929  
89              
90             ########################################################################
91             # package variables
92             ########################################################################
93              
94             *VERSION = \$Object::Trampoline::VERSION;
95              
96             our $AUTOLOAD = '';
97              
98             ########################################################################
99             # AUTOLOAD is the only public interface
100             ########################################################################
101              
102             AUTOLOAD
103             {
104             # this version does slightly more work since it
105             # has to put using the module into the caller's
106             # class before calling the constructor.
107             #
108             # the eval is unavoidable since use requires the
109             # module's package name as a bareword.
110              
111 4     4   1424 my ( undef, $proto, @argz ) = @_;
112              
113 4 100       13 $proto
114             or croak "Object::Trampoline::Use: false prototype.";
115              
116             # sanity check: the prototype is words glued together with
117             # double-colons. not perfect but worthwhile since most
118             # injection attempts will at least require a path or space.
119              
120 1         4 for( split '::' => $proto )
121             {
122 1 50       9 /\W/ and croak
123             "Botched trampoline_use: '$_' invalid module name";
124             }
125              
126             # Note: add a sanity pre-check w/ warning for non-existant
127             # module.
128              
129 1         3 my $method = ( split /::/, $AUTOLOAD )[ -1 ];
130 1         3 my $caller = caller;
131              
132 1         4 my $init
133             = qq
134             {
135             package $caller;
136             use $proto
137             };
138              
139             my $sub =
140             sub
141             {
142 1 50   1   98 eval "$init"
  1     1   616  
  0            
  0            
143             or croak "Failed: $init\n$@";
144            
145 0         0 $proto->$method( @argz )
146 1         5 };
147              
148 1         5 bless $sub, 'Object::Trampoline::Bounce'
149             }
150              
151             ########################################################################
152             # where the object ends up. All this does is possibly use the package
153             # then construct the object and dispatch the call to whatever the
154             # caller was looking for -- which may fail if the package doesn't
155             # implement the method.
156             #
157             # $_[0] = $_[0]->() replaces the trampoline argument
158             # with the real thing by calling its constructor -- call
159             # by reference is a Very Good Thing.
160             #
161             # after that it can be shifted off and used to access
162             # the method. note that this is necessary in order
163             # to allow for classes which implement their methods
164             # via AUTOLOAD (which will defeat using $obj->can( $name )).
165             #
166             # note that it's up to the caller to deal with any exceptions
167             # that come out of calling the method.
168             #
169             # goto is a more effecient way to get there if the class
170             # has an explicit method for handling the call; otherwise
171             # use the name to dispatch the call.
172              
173             package Object::Trampoline::Bounce;
174              
175 4     4   26 use v5.12;
  4         8  
176              
177 4     4   12 use Carp;
  4         3  
  4         176  
178              
179 4     4   12 use Scalar::Util qw( blessed );
  4         8  
  4         346  
180 4     4   1665 use Symbol qw( qualify_to_ref );
  4         2506  
  4         920  
181              
182             *VERSION = \$Object::Trampoline::VERSION;
183              
184             our $AUTOLOAD = '';
185              
186             AUTOLOAD
187             {
188             # caller gets back any execption as-is.
189              
190 8 50   8   927 $_[0] = $_[0]->()
191             or croak "Failed constructor";
192              
193 7 50       113 my $class = blessed $_[0]
194             or croak "Failed constructor: '$_[0]' not blessed";
195              
196 7         17 my $method = ( split /::/, $AUTOLOAD )[ -1 ];
197              
198 7 50       38 if( my $sub = $class->can( $method ) )
199             {
200 7         30 goto &$sub
201             }
202             else
203             {
204             # deal with autoloaded methods, or die trying...
205              
206 0         0 my $obj = shift;
207              
208 0         0 $obj->$method( @_ )
209             }
210             }
211              
212             # re-route methods from UNIVERSAL through the bounce.
213              
214             for my $name ( keys %{ $::{ 'UNIVERSAL::' } } )
215             {
216             *{ qualify_to_ref $name }
217             = sub
218             {
219 4     4   17 $AUTOLOAD = $name;
220            
221 4         7 goto &AUTOLOAD
222             };
223             }
224              
225             # stub destroy dodges AUTOLOAD for unused trampolines.
226              
227       0     DESTROY {}
228              
229             # keep require happy
230              
231             1
232              
233             __END__