Требуется взлом, потому что require
(и, следовательно use
) и компилирует, и выполняет модуль перед возвратом.
То же самое и для eval
. eval
не может быть использован для компиляции кода без его выполнения.
Наименее навязчивое решение, которое я нашел, было бы переопределить DB::postponed
. Это вызывается перед оценкой скомпилированного необходимого файла. К сожалению, он вызывается только при отладке ( perl -d
).
Другое решение состоит в том, чтобы прочитать файл, изменить его и оценить измененный файл, как показано ниже:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Вышеуказанное не правильно установлено %INC
, оно портит имя файла, используемое в предупреждениях и т. Д., Оно не вызывает DB::postponed
и т. Д. Следующее является более надежным решением:
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;
Я использовал UNITCHECK
(который вызывается после компиляции, но перед выполнением), потому что я добавлял переопределение (использование unread
), а не считывал весь файл и добавлял новое определение. Если вы хотите использовать этот подход, вы можете получить дескриптор файла, чтобы вернуться с помощью
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Престижность @Grinnz для упоминания @INC
крючков.
Foo::bar
, ноuse Foo
будет запускать как фазу компиляции (панель переопределения, если там что-то было ранее определено), так и фазу времени выполнения Foo. Единственное, о чем я могу думать, - это хакерский хак,@INC
чтобы изменить способ загрузки Foo.