File Coverage

lib/load.pm
Criterion Covered Total %
statement 36 156 23.0
branch 11 122 9.0
condition 1 17 5.8
subroutine 8 14 57.1
pod 0 1 0.0
total 56 310 18.0


line stmt bran cond sub pod time code
1             package load;
2              
3             $VERSION= '0.23';
4              
5             #-------------------------------------------------------------------------------
6             # No, we're NOT using strict here. There are several reasons, the most
7             # important being that strict bleeds into the string eval's that load.pm
8             # is doing, causing compilation errors in all but the most simple modules.
9             # If you _do_ want stricture as a developer of load.pm, simply de-activate
10             # the lines of the BEGIN block below here
11             #-------------------------------------------------------------------------------
12 1     1   290 BEGIN { # We're fooling the Kwalitee checker into thinking we're strict
13 1     1   33271 use strict;
  1         2  
  1         24  
14             }
15              
16             # do this at compile time
17             my $now;
18             BEGIN {
19              
20             # make sure we have warnings or dummy warnings for older Perl's
21 1 50   1   6 eval { require warnings } or do { $INC{'warnings.pm'} = '' };
  1         9  
  0         0  
22              
23             # set flag indicating whether everything should be loaded immediately
24 1   50     8 $now= $ENV{'LOAD_NOW'} || 0; # environment var undocumented for now
25              
26             # "ifdef" is loaded, can we use it?
27 1 50       3 if (defined $ifdef::VERSION) {
28 0 0       0 die "Must have 'ifdef' version 0.07 or higher to handle on demand loading\n"
29             if $ifdef::VERSION < 0.07;
30 0         0 *IFDEF= sub () { 1 };
31             }
32              
33             # ifdef not loaded
34             else {
35 1         2 *IFDEF= sub () { 0 };
36             }
37              
38             # we're supposed to trace
39 1 50       4 if ($ENV{'LOAD_TRACE'}) {
40 0         0 *TRACE= sub () { 1 };
41 0         0 eval <<'EOD'; # only way to ensure it isn't there when we're not tracing
42             sub _trace {
43             my $tid = $threads::VERSION ? ' ['.threads->tid.']' : '';
44             warn "load$tid: ",$_[0],$/;
45             } #_trace
46             EOD
47             }
48              
49             # we're not supposed to trace
50             else {
51 1         1 *TRACE= sub () { 0 };
52             }
53              
54             # make sure we intercept ->can
55 1     1   5 no warnings 'redefine';
  1         2  
  1         111  
56 1         2 my $can= \&UNIVERSAL::can;
57             *UNIVERSAL::can= sub {
58 6199 50   6199   26865 &{$can}( @_ ) || (ref( $_[0] ) ? undef : _can( @_ ))
  6199 100       175659  
59 1         453 };
60             } #BEGIN
61              
62             # hash with modules that should be used extra, keyed to package
63             my %use;
64              
65             # satisfy -require-
66             1;
67              
68             #-------------------------------------------------------------------------------
69             #
70             # Class Methods
71             #
72             #-------------------------------------------------------------------------------
73             # IN: 1 class (ignored)
74             # 2 package for which to add additional use-s
75             # 3 additional module to be used
76              
77 0   0 0 0 0 sub register { $use{ $_[1] }= ( $use{ $_[1] } || '' ) . "use $_[2];" } #register
78              
79             #-------------------------------------------------------------------------------
80             #
81             # Standard Perl Features
82             #
83             #-------------------------------------------------------------------------------
84             # IN: 1 class (ignored)
85             # 2..N various parameters
86              
87             sub import {
88 1     1   10 my $class = shift;
89 1         5 my ($module,undef,$lineno) = caller();
90              
91             # need to check / handle parameters specified
92 1 50       4 if (@_) {
    0          
93 1         2 my $inmain= ( $module eq 'main' );
94 1         2 my $autoload= !$inmain;
95 1         2 my $scan= 1;
96 1         2 my $thisnow= $now;
97              
98             # check each parameter separately
99 1         2 foreach (@_) {
100              
101             # handle "now"
102 1 50       4 if ( $_ eq 'now' ) {
    50          
    0          
    0          
    0          
    0          
103 0 0       0 ( $inmain ? $now= $thisnow : $thisnow )= $scan= 1;
104             }
105              
106             # handle "ondemand"
107             elsif ( $_ eq 'ondemand' ) {
108 1 50       3 ( $inmain ? $now= $thisnow : $thisnow )= 0;
109 1         2 $scan= 1;
110             }
111              
112             # want to export AUTOLOAD sub (AUTOLOAD = AutoLoader compatible)
113             elsif ( m#^(?:autoload|AUTOLOAD)$# ) {
114 0 0       0 die "Can not $_ in main namespace" if $inmain;
115 0         0 $autoload= 1;
116             }
117              
118             # don't want to scan now
119             elsif ( $_ eq 'dontscan' ) {
120 0 0       0 ( $inmain ? $now= $thisnow : $thisnow )= $scan= 0;
121             }
122              
123             # want to inherit AUTOLOAD
124             elsif ( $_ eq 'inherit' ) {
125 0 0       0 die "Can not inherit in main namespace" if $inmain;
126 0         0 $autoload= 0;
127             }
128              
129             # want to enable AutoLoader mode
130             elsif ( $_ eq 'AutoLoader' ) {
131 0 0       0 die "Can only activate AutoLoader emulation mode from script"
132             if !$inmain;
133              
134             # did not emulate AutoLoader before
135 0 0 0     0 if ( !$INC{'AutoLoader.pm'} or
136             $INC{'AutoLoader.pm'} ne $INC{__PACKAGE__.'.pm' } ) {
137 0         0 *AutoLoader::import= \&import;
138 0         0 *AutoLoader::AUTOLOAD= \&AUTOLOAD;
139              
140             # mark as loaded now, owned by us
141 0         0 $INC{'AutoLoader.pm'}= $INC{__PACKAGE__.'.pm'};
142             }
143             }
144              
145             # huh?
146             else {
147 0         0 die "Don't know how to handle $_";
148             }
149             }
150              
151             # in a module, scan it if necessary
152 1 50       13 if ( !$inmain ) {
153 0 0         _scan( $module, $thisnow ) if $scan;
154 1     1   5 no strict 'refs';
  1         1  
  1         89  
155 0 0         *{$module.'::AUTOLOAD'}= \&AUTOLOAD if $autoload;
  0            
156             }
157             }
158              
159             # called from a script / command line, huh?
160             elsif ( $module eq 'main' ) {
161 0 0         die "Does not make sense to just 'use $class;' from your script"
162             if $lineno;
163             }
164              
165             # no parameters, scan the source
166             else {
167 0           _scan( $module );
168              
169             # export AUTOLOAD if called here
170 0 0         if ( $class eq __PACKAGE__ ) {
171 1     1   5 no strict 'refs';
  1         2  
  1         2179  
172 0           *{$module.'::AUTOLOAD'}= \&AUTOLOAD;
  0            
173             }
174             }
175             } #import
176              
177             #-------------------------------------------------------------------------------
178              
179             sub AUTOLOAD {
180              
181             # go execute intended AUTOLOAD if possible
182 0     0     $load::AUTOLOAD =~ m#^(.*)::(.*?)$#;
183 0 0         goto &{$load::AUTOLOAD} if _can( $1, $2 );
  0            
184              
185             # nothing to do if unknown DESTROY
186 0 0         return if $2 eq 'DESTROY';
187              
188             # huh?
189 0           my ( $package, $filename, $line )= caller;
190 0           die "Undefined subroutine &$load::AUTOLOAD called at $filename line $line\n";
191             } #AUTOLOAD
192              
193             #-------------------------------------------------------------------------------
194             #
195             # Internal Subroutines
196             #
197             #-------------------------------------------------------------------------------
198             # _scan
199             #
200             # Set up / load given module
201             #
202             # IN: 1 module to scan (AAA::BBB)
203             # 2 optional: flag to load everything now
204              
205             sub _scan {
206 0     0     my $module= shift;
207 0 0         my $loadnow= defined( $_[0] ) ? shift : $now;
208              
209             # make sure we won't clobber sensitive system vars
210 0           local $_= \my $foo; # make sure $_ is localized properly
211 0           local( $!, $@ );
212              
213             # open the file to read
214 0 0         my $file= _filename( $module )
215             or die "Could not find file for '$module'";
216 0 0         open( VERSION, "<$file" ) # use VERSION as glob to save memory
217             or die "Could not open file '$file' for '$module': $!";
218 0 0         binmode VERSION # needed for Windows systems, apparently
219             or die "Could not set binmode on '$file': $!";
220              
221             # initializations
222 0           my $line= 0;
223 0           my $pod= 0;
224 0           my $package= '';
225 0           &ifdef::reset if IFDEF; # should get optimized away if not needed
226              
227             # look through all lines
228 0           while ( readline VERSION ) {
229 0           &ifdef::oneline if IFDEF; # should get optimized away if not needed
230 0           $line++;
231              
232             # inside pod
233 0 0         $pod= !m#^=cut#, next if m#^=\w#;
234 0 0 0       next if $pod or m#^\s*\##;
235              
236             # we're done
237 0 0         last if m#^__END__#;
238              
239             # not a package
240 0 0         next if !m#^package\s+([\w:]+)\s*;#;
241              
242             # found a package
243 0 0         die "Found package '$1' after '$package'" if $package;
244 0           $package= $1;
245             }
246              
247             # huh?
248 0 0         die "Could not find package name" if !$package;
249 0 0         die "Found package $package inside '$file'" if $package ne $module;
250              
251             # initializations
252 0           my $endline= $line+1;
253 0           my $endstart= tell VERSION;
254              
255             # loading now
256 0 0         if ($loadnow) {
257 0           _trace( "now $module, line $endline (offset $endstart, onwards)" )
258             if TRACE;
259              
260             # load the source
261 0           my $source= <<"SRC";
262             package $module;
263             no warnings;
264             #line $endline "$file (loaded now from offset $endstart)"
265             SRC
266 0           $source .= do { local $/; readline(VERSION) =~ m#^(.*)$#s; $1 };
  0            
  0            
  0            
267 0           eval ( IFDEF ? ifdef::process($source) : $source );
268 0 0         die "Error evaluating source: $@" if $@;
269             }
270              
271             # loading on demand
272             else {
273 0           my $start;
274 0           my $sub= '';
275 0           my $subline;
276             my $length;
277              
278             # process all lines
279 0           while ( readline VERSION ) {
280 0           $length= length if IFDEF;
281 0           &ifdef::oneline if IFDEF;
282 0           $line++;
283              
284             # inside pod
285 0 0         $pod= !m#^=cut#, next if m#^=\w#;
286 0 0 0       next if $pod or m#^\s*\##;
287              
288             # we're done
289 0 0         last if m#^__END__#;
290              
291             # huh?
292 0 0         die "Only one package per file: found '$1' after '$package'"
293             if m#^package\s+([\w:]+)\s*;#;
294              
295             # not at next sub yet
296 0 0         next unless m#^sub\s+([\w:]+)#;
297              
298             # remember where previous sub starts, if any
299 0           my $seek= tell(VERSION) - ( IFDEF ? $length : length );
300 0 0         _store( $module, $sub, $subline, $start, $seek - $start ) if $sub;
301              
302             # set up for next iteration
303 0           $sub= $1;
304 0 0         die "Cannot handle fully qualified subroutine '$sub'\n"
305             if $sub =~ m#::#;
306 0           $subline= $line;
307 0           $start= $seek;
308             }
309              
310             # store rest as a sub, if any
311             _store(
312 0 0         $module,
    0          
313             $sub,
314             $subline,
315             $start,
316             ( defined() ? tell(VERSION) - length() : -s VERSION ) - $start
317             ) if $sub;
318             }
319              
320             # we're done
321 0           $load::AUTOLOAD{$module}= undef;
322 0           close VERSION;
323              
324 0           return;
325             } #_scan
326              
327             #-------------------------------------------------------------------------------
328             # _filename
329             #
330             # Return filename of module
331             #
332             # IN: 1 module name (AAA::BBB)
333             # OUT: 1 filename (/..../AAA/BBB.pm) or undef if not known
334              
335             sub _filename {
336              
337             # return filename from %INC
338 0     0     ( my $key= $_[0] ) =~ s#::#/#g;
339 0           my $filename= $INC{"$key.pm"};
340 0 0         return $filename if !ref $filename;
341              
342             # return result of call instead
343 0           return $filename->("$key.pm");
344             } #_filename
345              
346             #-------------------------------------------------------------------------------
347             # _store
348             #
349             # Store information about subroutine in memory for later usage
350             #
351             # IN: 1 module name
352             # 2 subroutine name (not fully qualified)
353             # 3 line number where sub starts
354             # 4 offset where sub starts
355             # 5 number of bytes to read
356              
357             sub _store {
358 0     0     _trace( "store $_[0]::$_[1], line $_[2] (offset $_[3], $_[4] bytes)" )
359             if TRACE;
360              
361             # huh?
362 0           eval "package $_[0]; sub $_[1]";
363 0 0         die "Could not create stub: $@\n" if $@;
364              
365             # store the data
366 0           $load::AUTOLOAD{ $_[0], $_[1] }= pack( 'w3', $_[2], $_[3], $_[4] )
367             } #_store
368              
369             #-------------------------------------------------------------------------------
370             # _can
371             #
372             # Our version of ->can
373             #
374             # IN: 1 module to load subroutine from
375             # 2 subroutine to load
376             # OUT: 1 reference to subroutine (if exists and loaded, else undef)
377              
378             sub _can {
379 0     0     my ( $module, $sub )= @_;
380              
381             # nothing to do here
382 0 0         return if $module eq 'main';
383              
384             # scan file if we need to
385 0 0         _scan( $module ) unless exists $load::AUTOLOAD{$module};
386              
387             # huh? unknown sub?
388 0   0       my ( $subline, $start, $length )=
389             unpack( 'w3', $load::AUTOLOAD{ $module, $sub } || '' );
390 0 0         return if !$start;
391              
392             # seek in the file where the source lives
393 0           local( $!, $@ );
394 0 0         my $file= _filename( $module )
395             or die "Could not find file for '$module.pm'";
396 0 0         open( VERSION, "<$file" ) # use VERSION glob to conserve memory
397             or die "Could not open file '$file' for '$module.pm': $!";
398 0 0         binmode VERSION # needed for Windows systems, apparently
399             or die "Could not set binmode on '$file': $!";
400 0 0         seek( VERSION, $start, 0 )
401             or die "Could not seek to $start for $module\::$sub";
402              
403             # set up evallable pre-amble
404 0           _trace( "ondemand ${module}::$sub, line $subline (offset $start, $length bytes)" ) if TRACE;
405 0   0       my $use= $use{$module} || '';
406 0           my $source= <<"SRC";
407             package $module;
408             no warnings;$use
409             #line $subline "$file (loaded on demand from offset $start for $length bytes)"
410             SRC
411              
412             # get the stuff
413 0           my $read= read( VERSION, $source, $length, length($source) );
414 0 0         die "Error reading source: only read $read bytes instead of $length"
415             if $read != $length;
416 0           close VERSION;
417              
418             # initializations
419 0           &ifdef::reset if IFDEF; # make sure "ifdef" starts afresh
420 0           my $original= $source;
421              
422             # eval untainted copy
423 0           $source =~ m#^(.*)$#s;
424 0           $source= IFDEF ? ifdef::process($1) : $1;
425 0           eval $source;
426 0 0         die "load: $@\n$original====================\n$source" if $@;
427              
428             # done this one
429 0           delete $load::AUTOLOAD{ $module, $sub };
430              
431 0           return \&{ $module . '::' . $sub };
  0            
432             } #_can
433              
434             #-------------------------------------------------------------------------------
435              
436             __END__