Overwriting a function defined in a module but before used in its runtime phase?

Let's take something very simple,

# Foo.pm
package Foo {
  my $baz = bar();
  sub bar { 42 };  ## Overwrite this
  print $baz;      ## Before this is executed
}

Is there anyway that I can from test.pl run code that changes what $baz is set to and causes Foo.pm to print something else to the screen?

# maybe something here.
use Foo;
# maybe something here

Is it possible with the compiler phases to force the above to print 7?

A hack is required because require (and thus use) both compiles and executes the module before returning.

Same goes for eval. eval can't be used to compile code without also executing it.

The least intrusive solution I've found would be to override DB::postponed. This is called before evaluating a compiled required file. Unfortunately, it's only called when debugging (perl -d).

Another solution would be to read the file, modify it and evaluate the modified file, kinda like the following does:

use File::Slurper qw( read_binary );

eval(read_binary("Foo.pm") . <<'__EOS__')  or die $@;
package Foo {
   no warnings qw( redefine );
   sub bar { 7 }
}
__EOS__

The above doesn't properly set %INC, it messes up the file name used by warnings and such, it doesn't call DB::postponed, etc. The following is a more robust solution:

use IO::Unread  qw( unread );
use Path::Class qw( dir );

BEGIN {     
   my $preamble = '
      UNITCHECK {
         no warnings qw( redefine );
         *Foo::bar = sub { 7 };
      }
   ';    

   my @libs = @INC;
   unshift @INC, sub {
      my (undef, $fn) = @_;
      return undef if $_[1] ne 'Foo.pm';

      for my $qfn (map dir($_)->file($fn), @libs) {
         open(my $fh, '<', $qfn)
            or do {
               next if $!{ENOENT};
               die $!;
            };

         unread $fh, "$preamble\n#line 1 $qfn\n";
         return $fh;
      }

      return undef;
   };
}

use Foo;

I used UNITCHECK (which is called after compilation but before execution) because I prepended the override (using unread) rather than reading in the whole file in and appending the new definition. If you want to use that approach, you can get a file handle to return using

open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;

Kudos to @Grinnz for mentioning @INC hooks.

The macro expander handles such nesting by shifting the phase level of the module* form so that its body starts at phase level 0, expanding, and then reverting the phase level shift; beware that this process can leave syntax objects as ' origin syntax property values out-of-sync with the expanded module.

Since the only options here are going to be deeply hacky, what we really want here is to run code after the subroutine has been added to the %Foo:: stash:

use strict;
use warnings;

# bless a coderef and run it on destruction
package RunOnDestruct {
  sub new { my $class = shift; bless shift, $class }
  sub DESTROY { my $self = shift; $self->() }
}

use Variable::Magic 0.58 qw(wizard cast dispell);
use Scalar::Util 'weaken';
BEGIN {
  my $wiz;
  $wiz = wizard(store => sub {
    return undef unless $_[2] eq 'bar';
    dispell %Foo::, $wiz; # avoid infinite recursion
    # Variable::Magic will destroy returned object *after* the store
    return RunOnDestruct->new(sub { no warnings 'redefine'; *Foo::bar = sub { 7 } }); 
  });
  cast %Foo::, $wiz;
  weaken $wiz; # avoid memory leak from self-reference
}

use lib::relative '.';
use Foo;

CALL_FUNCTION_CONFLICT_TYPE - Usually means that the way you've defined . one of your parameters is incompatible with the function module. Recheck your data declarations to make sure they're compatible with what . the function module wants. Reason and Prerequisites

This will emit some warnings, but prints 7:

sub Foo::bar {}
BEGIN {
    $SIG{__WARN__} = sub {
        *Foo::bar = sub { 7 };
    };
}

First, we define Foo::bar. It's value will be redefined by the declaration in Foo.pm, but the "Subroutine Foo::bar redefined" warning will be triggered, which will call the signal handler that redefines the subroutine again to return 7.

Because it is called after the module is fully imported, any submodules or other imported modules have their __init__ functions called before the __init__ of the enclosing module. Two typical uses of __init__ are calling runtime initialization functions of external C libraries and initializing global constants that involve pointers returned by

Here is a solution that combines hooking the module loading process with the readonly-making capabilities of the Readonly module:

$ cat Foo.pm 
package Foo {
  my $baz = bar();
  sub bar { 42 };  ## Overwrite this
  print $baz;      ## Before this is executed
}


$ cat test.pl 
#!/usr/bin/perl

use strict;
use warnings;

use lib qw(.);

use Path::Tiny;
use Readonly;

BEGIN {
    my @remap = (
        '$Foo::{bar} => \&mybar'
    );

    my $pre = join ' ', map "Readonly::Scalar $_;", @remap;

    my @inc = @INC;

    unshift @INC, sub {
        return undef if $_[1] ne 'Foo.pm';

        my ($pm) = grep { $_->is_file && -r } map { path $_, $_[1] } @inc
           or return undef;

        open my $fh, '<', \($pre. "#line 1 $pm\n". $pm->slurp_raw);
        return $fh;
    };
}


sub mybar { 5 }

use Foo;


$ ./test.pl   
5

check_phase: function: Used to perform scoreboard tasks that check for errors between expected and actual values from design: report_phase: function: Used to display result from checkers, or summary of other test objectives: final_phase: function: Typically used to do last minute operations before exiting the simulation

I have revised my solution here, so that it no longer relies on Readonly.pm, after learning that I had missed a very simple alternative, based on m-conrad's answer, which I have reworked into the modular approach that I had started here.

Foo.pm (Same as in the opening post)

package Foo {
  my $baz = bar();
  sub bar { 42 };  ## Overwrite this
  print $baz;      ## Before this is executed
}
# Note, even though print normally returns true, a final line of 1; is recommended.

OverrideSubs.pm Updated

package OverrideSubs;

use strict;
use warnings;

use Path::Tiny;
use List::Util qw(first);

sub import {
    my (undef, %overrides) = @_;
    my $default_pkg = caller; # Default namespace when unspecified.

    my %remap;

    for my $what (keys %overrides) {
        ( my $with = $overrides{$what} ) =~ s/^([^:]+)$/${default_pkg}::$1/;

        my $what_pkg  = $what =~ /^(.*)\:\:/ ? $1 : $default_pkg;
        my $what_file = ( join '/', split /\:\:/, $what_pkg ). '.pm';

        push @{ $remap{$what_file} }, "*$what = *$with";
    }

    my @inc = grep !ref, @INC; # Filter out any existing hooks; strings only.

    unshift @INC, sub {
        my $remap = $remap{ $_[1] } or return undef;
        my $pre = join ';', @$remap;

        my $pm = first { $_->is_file && -r } map { path $_, $_[1] } @inc
            or return undef;

        # Prepend code to override subroutine(s) and reset line numbering.
        open my $fh, '<', \( $pre. ";\n#line 1 $pm\n". $pm->slurp_raw );
        return $fh;
   };
}

1;

test-run.pl

#!/usr/bin/env perl

use strict;
use warnings;

use lib qw(.); # Needed for newer Perls that typically exclude . from @INC by default.

use OverrideSubs
    'Foo::bar' => 'mybar';

sub mybar { 5 } # This can appear before or after 'use OverrideSubs', 
                # but must appear before 'use Foo'.

use Foo;

Run and output:

$ ./test-run.pl 
5

Each module has its own private symbol table, which serves as the global symbol table for all objects defined in the module. Thus, a module creates a separate namespace , as already noted. The statement import <module_name> only places <module_name> in the caller’s symbol table.

An attempt has been made to start a new process before the current process has finished its bootstrapping phase. This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module: if _name_ == '_main_': freeze_support()

module_globals, if supplied, should be the global namespace in use by the code for which the warning is issued. (This argument is used to support displaying source for modules found in zipfiles or other non-filesystem import sources). source, if supplied, is the destroyed object which emitted a ResourceWarning.

if an autouse fixture is defined in a test module, all its test functions automatically use it. if an autouse fixture is defined in a conftest.py file then all tests in all test modules below its directory will invoke the fixture.

Comments
  • It's not an internal function - it's accessible globally as Foo::bar, but the use Foo will run both the compilation phase (redefining bar if anything was previously defined there) and the runtime phase of Foo. The only thing I can think of would be a deeply hacky @INC hook to modify how Foo is loaded.
  • You want to redefine the function altogether, yes? (Not just change part of its operation, like that print?) Are there specific reasons for redefining before runtime? The title asks for that but the question body doesn't say/elaborate. Sure you can do that but I am not sure of the purpose so whether it would fit.
  • @zdim yes there are reasons. I want be able to redefine a function used in another module before the runtime phase of that module. Exactly what Grinnz suggested.
  • @Grinnz Is that title better?
  • A hack is required. require (and thus use) both compiles and executes the module before returning. Same goes for eval. eval can't be used to compile code without also executing it.
  • Wellll that's a hack if I've ever seen one.
  • This isn't possible without a hack. If the subroutine were called in another subroutine, it would be much easier.
  • That will only work if the module being loaded has warnings enabled; Foo.pm doesn't enable warnings and thus this will never be called.
  • @szr: So call it with perl -w.
  • @choroba: Yes, that would work, as -w will enable warnings everywhere, iirc. But my point is that you can't be sure how a user will run that. For example, one-liners typically run sans strictures or warnings.
  • @ikegami Thanks, I have made the changes that you recommended. Good catch.
  • BEGIN { *Foo::bar = sub () { 7 } } is better written as sub Foo::bar() { 7 }