Following up on my post on this subject a couple of months ago, here is a
proof-of-concept drop-in replacement for Apache::Registry that eliminates
the "my() Scoped Variable in Nested Subroutine" problem.

It requires PERL5OPT = "-d" and PERL5DB = "sub DB::DB {}" environment
variables set when starting the mod_perl-enabled httpd. This enables the
%DB::sub hash table that holds subroutine start and end line info. I
presume that this has some negative (marginal?) impact on performance. If
someone knows of a better way to reliably figure out where a subroutine
starts and ends, please let me know.

The processed code will probably generate harmless warnings if you have
them turned on.

I'm interested if there is support for turning this into a
production-quality module. Better yet, perhaps the existence of such an
ridiculous hack could spur the perl maintainers to provide a cleaner
solution.

--Chris

package My::CleanerRegistry;
use base Apache::RegistryNG;
use strict;

my %skip_subs = map { $_ => 1 } qw( handler BEGIN END );

sub compile {
    my ($pr, $eval) = @_;
    $eval ||= $pr->{'sub'};
    $pr->SUPER::compile($eval);
    my $package = quotemeta($pr->namespace);
    my @lines = split /\n/, $$eval;
    foreach my $sub (grep /^$package/, keys %DB::sub) {
        my ($name) = $sub =~ /:([^:]+)$/;
        next if $skip_subs{$name};
        my ($start, $end) = $DB::sub{$sub} =~ /:(\d+)-(\d+)$/;
        $start++, $end++;
        $lines[$start] =~ s:(sub[^\{]+\{):$1&\{sub\{:;
        $lines[$end]   =~ s:\}(?!.*\})$:\}\}\}:;
    }
    $$eval = join "\n", @lines;
    $pr->flush_namespace($package);
    my $rv = $pr->SUPER::compile($eval);
}

1;



On Wed, 16 Aug 2000 [EMAIL PROTECTED] wrote:

> Due to forgetfulness I was recently bitten by the infamous "my() Scoped
> Variable in Nested Subroutines" problem using Apache::Registry, and it got
> me thinking about whether it is fixable.
> 
> From the diagnostics:
>   This problem can usually be solved by making the inner subroutine
>   anonymous, using the sub {} syntax.  When inner anonymous subs that
>   reference variables in outer subroutines are called or referenced, they
>   are automatically rebound to the current values of such variables.
> 
> I think it should be possible for Registry to pre-process the source code
> to turn all top-level named subroutines into sub refs. For example,
> convert subroutines of the form
> 
>   sub foo { <CODE> }
> 
> into
> 
>   sub foo { &{ sub { <CODE> } } }
> 
> Are there cases for which this would not work? I have a sneaking suspicion
> that I am missing something important. If it is a reasonable solution, I
> imagine there are better ways to do this transformation than fancy
> regexps? Parse/modify/deparse?
> 
> Below is a processed version of the increment_counter example from the
> guide that works as expected.
> 
> --Chris
> 
>   #!/usr/bin/perl -w
>   use strict;
> 
>   for (1..3){
>       print "run: [time $_]\n";
>       run();
>   }
> 
>   sub run {
> 
>       my $counter = 0;
> 
>       increment_counter();
>       increment_counter();
> 
>       sub increment_counter {&{sub{
>           $counter++;
>           print "Counter is equal to $counter !\n";
>       }}}
> 
>   } # end of sub run
> 
> 

Reply via email to