Subject: Re: Is Require an Executable Statement?

From: [email-masked] (Tom Christiansen)
Date: 13 Feb 92 06:07:50 GMT

>From the keyboard of [email-masked] (Root Boy Jim):
:Or is it compile time, as 'sub' seems to be?

It's executable.  Perl has only 3 compile-time things, all forms
of declarations:

    1)   subroutine definitions
    2)   format definitions
    3)   package statements

:Also, I have heard people speak of 'autoloading'
:in perl. Anyone have a good example?

Many.

Here's a simple set for external autoloading:

    sub swapnload {
	local($fun,$file) = @_;
	local($tmp) = substr($_stub{$fun}, 7*4, 4);   
	substr($_stub{$fun}, 7*4, 4) = substr($_main{$fun}, 7*4, 4);
	substr($_main{$fun}, 7*4, 4) = $tmp;
	require $file;
    } 

    sub autoload {
	local($fun, $file) = @_;
	$file = "./$fun.pl" unless $file;
	eval <<EOCODE;
	    sub $fun { 
		warn("autoloading $fun from $fun.pl...\n");
		# local(*stub'$fun); 
		&swapnload("$fun", "$file"); 
		&$fun; 
	}
    EOCODE
	die "autoload failure creating stub for $fun: $@" if $@;
    } 

And here's a set for internal autoloading:

    sub dataload {
	local($fun) = (caller(1))[3]; 

	local($addr);

	die("No address for $fun") 
	    unless $addr = $Func_Addrs{$fun};

	die("$fun should already be loaded") 
	    if $addr < $Data_Start;

	seek(DATA,$addr,0);

	local($code,$_);

	while (<DATA>) {
	    $code .= $_;
	    last if /^\}/;

	die qq(No "sub $fun" in code at $addr:\n$code\n) 
	    unless $code =~ /^sub\s+$fun/;

	local($tmp) = substr($_stub{$fun}, 7*4, 4);   
	substr($_stub{$fun}, 7*4, 4) = substr($_main{$fun}, 7*4, 4);
	substr($_main{$fun}, 7*4, 4) = $tmp;

	eval $code;

	die $@ if $@;

	&$fun;
    }

    sub fetch_function_addrs {
	local($count) = 0;
	local($_);

	$Data_Start = tell(DATA);

	print STDERR "gathering functions from $Data_Start: " ;

	seek(DATA,0,0);

	for (local($pos); <DATA>; $pos = tell) {
	    next unless /^\s*sub\s+((\w+')?(\w+))/;
	    $Func_Addrs{$1} = $pos;
	    $count++;
	}

	print STDERR "found $count functions\n";
	seek(DATA,$Data_Start,0);
    }

The prep phase for that looks like this:

    &fetch_function_addrs();
    &main();  # main goes here
    sub main'foo { &dataload; }
    sub main'bar { &dataload; }
__END__
    sub main'foo {
	print "i am foo\n";
    }
    sub main'bar {
	print "i am bar\n";
    }

To cache the addrs, I do this in plum:

    sub fetch_function_addrs {
	local($count);
	local($_);
	local($hischecksum, $mychecksum);
	local($update);

	$Data_Start = tell(DATA);

	print STDERR "gathering functions: " 
	    if defined $ENV{'debug_plum'};

	seek(DATA,0,0);
	{ local($/); $mychecksum = unpack("%16C*", <DATA>); }

	if ((stat(DATA))[9] > (stat($ADDR_CACHE))[9]) {
	    print STDERR "addrcache out of date "  if defined $ENV{'debug_plum'};
	    $update++;
	} elsif (!open(ADDRS, $ADDR_CACHE)) {
	    print STDERR "can't open addrcache $ADDR_CACHE: $! "  
		if defined $ENV{'debug_plum'};
	    $update++;
	} else {
	    ($hischecksum) = <ADDRS> =~ /^CHECKSUM (\d+)/;
	    if ($mychecksum != $hischecksum) {
		print STDERR "checksum mismatch "  if defined $ENV{'debug_plum'};
		$update++;
	    }
	} 

	unless ($update) {
	    print STDERR "yippee, snagged addrcache " 
		if defined $ENV{'debug_plum'};
	    split, $Func_Addrs{$_[0]} = $_[1] while <ADDRS>;
	    $count = $. - 1;
	    close ADDRS;
	} else {
	    seek(DATA,0,0);

	    for (local($told); <DATA>; $told = tell) {
		next unless /^\s*sub\s+((\w+')?(\w+))/;
		warn "duplicate func $1 @ $told" if defined $Func_Addrs{$1};
		$Func_Addrs{$1} = $told;
		$count++;
	    }

	    unless (open(ADDRS, ">$ADDR_CACHE")) {
		print STDERR "couldn't update addrcache: $! "
		    if defined $ENV{'debug_plum'};
	    }  else { 
		print STDERR "updating addrcache " if defined $ENV{'debug_plum'};
		local($x,$y);
		print ADDRS 'CHECKSUM ', $mychecksum, "\n";
		print ADDRS $x, ' ', $y, "\n" while ($x, $y) = each %Func_Addrs;
		close ADDRS;
	    } 
	}

	print STDERR "found $count functions\n" if defined $ENV{'debug_plum'};

	seek(DATA,$Data_Start,0);
    }

Those good enough examples?  I also have tools to split and rebuild
random perl programs into either autoloading or dataloading forms, doing
useful things like being careful to keep module initializations up before
&main() instead of in DATA, etc.

--tom