Злому потрібен, оскільки 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.