# ================================================================== # Gossamer Threads Module Library - http://gossamer-threads.com/ # # GT::AutoLoader # Author: Jason Rhinelander # $Id: AutoLoader.pm,v 1.13 2005/03/21 06:57:58 jagerman Exp $ # # Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. # ================================================================== package GT::AutoLoader; use vars qw($AUTOLOAD %LOG %PACKAGES); use strict qw/vars subs/; # no strict 'refs' - we need several soft references here. sub import { shift; # Discard the package, as 'use GT::AutoLoader' calls GT::AutoLoader->import(ARGS) my %opts = @_; my $pkg = caller; ++$PACKAGES{$pkg}; if ($opts{LOG} and ref $opts{LOG} eq 'CODE') { $LOG{$pkg} = delete $opts{LOG}; # Everything that requests a log will get one for all modules } delete $opts{NAME} if $opts{NAME} and $opts{NAME} eq 'AUTOLOAD'; # Allows "if ($opts{NAME})" later on. my $COMPILE; *{$pkg . ($opts{NAME} ? "::$opts{NAME}" : '::AUTOLOAD')} = sub { if ($opts{NAME} or !$AUTOLOAD) { # If they're using another name, it most likely means they are wrapping the AUTOLOAD, which means we have to check for $AUTOLOAD in their package. $AUTOLOAD = ${$pkg . '::AUTOLOAD'}; } my ($func) = $AUTOLOAD =~ /([^:]+)$/; # How odd - we use $GT::AutoLoader::AUTOLOAD, even though this is run in some other package if ($COMPILE = \%{$pkg . '::COMPILE'}) { if (defined $COMPILE->{$func}) { for (keys %LOG) { $LOG{$_}->($pkg, $func, 'COMPILE') } _compile($COMPILE, $pkg, $func); $AUTOLOAD = ''; goto &{"$pkg\::$func"}; } } if ($opts{NEXT}) { my ($pack, $func) = $opts{NEXT} =~ /(?:(.+)::)?([^:]+?)$/; $pack ||= $pkg; ${$pack . '::AUTOLOAD'} = $AUTOLOAD; my $next = "$pack\::$func"; $AUTOLOAD = ''; goto &$next; } # It doesn't exist in %COMPILE, which means we have to look through @ISA for another AUTOLOAD to pass this to if (my @inh = @{"$pkg\::ISA"}) { while (my $inh = shift @inh) { my $al = $inh . '::AUTOLOAD'; if (defined &$al) { $$al = "$pkg\::$func"; # Sets $Other::Package::AUTOLOAD $AUTOLOAD = ''; goto &$al; } elsif (my @isa = @{$inh . '::ISA'}) { unshift @inh, @isa; } } } my ($file, $line) = (caller)[1,2]; $AUTOLOAD = ''; die "$pkg ($$, GT::AutoLoader): Unknown method '$func' called at $file line $line.\n"; }; my $compile = "$pkg\::COMPILE"; *$compile = \%$compile; # Implements "use vars qw/%COMPILE/" for you 1; } BEGIN { if ($^C) { eval <<'CHECK'; sub CHECK { # ------------------------------------------------------------------------------ # In Perl 5.6+ this allows you to do: perl -cMMy::Module -e0 to make sure all # your %COMPILE subs compile. In versions of Perl prior to 5.6, this is simply # treated as a sub named "CHECK", which is never called. $^C is also 5.6+ # specific - whether or not you are running under "-c" compile_all(); } CHECK } } sub compile_all { my @pkg = @_; if (@pkg) { @pkg = grep +($PACKAGES{$_} or (warn "$_ is not loaded, does not use GT::AutoLoader, or is not a valid package" and 0)), @pkg; @pkg or die "No valid packages passed to compile_all()!"; } else { @pkg = keys %PACKAGES; } for my $pkg (@pkg) { my $COMPILE = \%{$pkg . '::COMPILE'} or next; for my $func (keys %$COMPILE) { _compile($COMPILE, $pkg, $func) if $COMPILE->{$func}; } } return 1; } sub _compile { # ------------------------------------------------------------------------------ # Compiles a subroutine from a module's %COMPILE into the module's package. # die()s if the subroutine cannot compile or still does not exist after # compiling. Takes three arguments: A reference to the packages %COMPILE hash, # the package, and the name of the function to load. # my ($COMPILE, $pkg, $func) = @_; my $linenum = ($COMPILE->{$func} =~ s/^(\d+)//) ? $1+1 : 0; eval "package $pkg;\n#line $linenum$pkg\::$func\n$COMPILE->{$func}"; if ($@) { die "Unable to load $pkg\::$func: $@" } if (not defined &{"$pkg\::$func"}) { die "Unable to load $pkg\::$func: Subroutine did not compile correctly (possible bad name)."; } undef $COMPILE->{$func}; # Leave the key in the compile hash so that things can test to see if it was defined in the compile hash return; } 1; __END__ =head1 NAME GT::AutoLoader - load subroutines on demand =head1 SYNOPSIS package GT::Module; use GT::AutoLoader; # You now have an AUTOLOAD subroutine that will check for entries in %COMPILE or package GT::OtherModule; use GT::AutoLoader(NAME => '_AUTOLOAD'); # Import AUTOLOAD as _AUTOLOAD, define our own AUTOLOAD sub AUTOLOAD { ... goto &_AUTOLOAD; } then: $COMPILE{sub} = __LINE__ . <<'END_OF_SUB'; sub method_name { ... } END_OF_SUB =head1 DESCRIPTION The B module works as a way to speed up your code. Currently, the only thing it does is scan for a %COMPILE hash in your package. If it finds it, it looks for the subroutine you called, and if found compiles and runs it. If unable to find a subroutine to compile in %COMPILE, B will scan your inheritance tree (@ISA) for another AUTOLOAD subroutine to pass this off to. If there isn't any, a fatal error occurs. To use B, in its standard behaviour, simply put: C in your module. When you use GT::AutoLoader, two things will happen. First, an C subroutine will be imported into your namespace that will automatically compile your subroutines only when they are needed, thus speeding up compile time. Secondly, a %COMPILE hash will be defined in your package, eliminating the need for you to: use vars qw/%COMPILE/; =head1 USE You can pass options to GT::AutoLoader to change the behaviour of the module. Currently, logging is the only option, however more options (perhaps including a different compiling scheme) will be added at some future point. Options are specified as import() arguments. For example: use GT::AutoLoader(OPTION => "value"); =over 4 =item NAME If you want to import the autoload subroutine as something other than 'Package::AUTOLOAD', the 'NAME' option should be used. Its value is the name to import as. For example, to import a GT::AutoLoader AUTOLOAD named _AUTOLOAD (this is useful when declaring your own AUTOLOAD behaviour, but still using GT::AutoLoader's behaviour as a fallback), you would do something like: use GT::AutoLoader(NAME => '_AUTOLOAD'); =item LOG Takes a code reference as its value. The code reference will be called three arguments - the package name, the name of the function, and the autoload method (Currently only 'COMPILE'). Note that this will be called for ALL autoloaded subroutines, not just the ones in your package. WARNING - you cannot put code in your log that relies on autoloaded methods - you'll end up throwing the program into an infinite loop. For example, to get a line of debugging after each subroutine is compiled, you could C like this: use GT::AutoLoader(LOG => sub { print "Compiled $_[1] in package $_[0]\n" }); =item NEXT Normally, GT::AutoLoader will look for another AUTOLOAD to call in your package's @ISA inheritance tree. You can alter this behaviour and tell GT::AutoLoader what to call next using the NEXT option. For example, if you have a sub _AUTOLOAD { } that you wanted to call if the method isn't found by GT::AutoLoader, you would use GT::AutoLoader like this: use GT::AutoLoader(NEXT => 'Package::Name::_AUTOLOAD'); The _AUTOLOAD function in your package will now be called if GT::AutoLoader can't load the method on its own. $AUTOLOAD will be set for you in whichever package the function you provide is in. Note that if you simply want to use an inherited AUTOLOAD, you B use this option; GT::AutoLoader will handle that just fine on its own. You may omit the package (Package::Name::) if the function is in your current package. =back =head1 compile_all A function exists in GT::AutoLoader to compile all %COMPILE-subroutines. By default (without arguments) compile_all() compiles every %COMPILE-subroutine in every package that has used GT::AutoLoader. You can, however, pass in a list of packages which compile_all() will check instead of compiling everything. Note that GT::AutoLoader will only compile %COMPILE-subroutines in packages that have used GT::AutoLoader, so if you specify package "Foo", but "Foo" hasn't used GT::AutoLoader, it will be ignored. You can do something like: GT::AutoLoader::compile_all(__PACKAGE__) if MOD_PERL; to have a GT::AutoLoader compile every %COMPILE-subroutine in the current package automatically under mod_perl, or you could add this code to your mod_perl startup file or test script: GT::AutoLoader::compile_all; Test scripts should definately use compile_all() to ensure that all subroutines compile correctly! =head1 REQUIREMENTS None. =head1 CAVEATS Due to the nature of Perl's AUTOLOAD handling, you must take care when using GT::AutoLoader in a subclass. In short, subclassed methods B be put into the %COMPILE hash. The problem is that since the subroutine does not exist in the package, Perl, while decending the inheritance tree, will not see it but will probably see the parent's method (unless nothing else has called the method, but you should never count on that), and call it rather than looking for your package's AUTOLOAD. This isn't to say that subclasses cannot use AUTOLOAD - just that subclasses cannot use autoloaded methods (%COMPILE-subroutines) if a method of the same name exists in the parent class. Autoloaded function calls are not affected. =head1 MAINTAINER Jason Rhinelander =head1 SEE ALSO L =head1 COPYRIGHT Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. http://www.gossamer-threads.com/ =head1 VERSION Revision: $Id: AutoLoader.pm,v 1.13 2005/03/21 06:57:58 jagerman Exp $ =cut