Давайте возьмем что-то очень простое,
# Foo.pm
package Foo {
my $baz = bar();
sub bar { 42 }; ## Overwrite this
print $baz; ## Before this is executed
}
Могу ли я в любом случае test.pl
запустить код, который изменяет то, что $baz
установлено, и заставляет Foo.pm
печатать что-то еще на экране?
# maybe something here.
use Foo;
# maybe something here
Возможно ли с фазами компилятора заставить вышеперечисленное печатать 7
?
perl
compilation
Эван Кэрролл
источник
источник
Foo::bar
, ноuse Foo
будет запускать как фазу компиляции (панель переопределения, если там что-то было ранее определено), так и фазу времени выполнения Foo. Единственное, о чем я могу думать, - это хакерский хак,@INC
чтобы изменить способ загрузки Foo.require
(и, следовательноuse
) как компилирует, так и выполняет модуль перед возвратом. То же самое и дляeval
.eval
не может быть использован для компиляции кода без его выполнения.Ответы:
Требуется взлом, потому что
require
(и, следовательноuse
) и компилирует, и выполняет модуль перед возвратом.То же самое и для
eval
.eval
не может быть использован для компиляции кода без его выполнения.Наименее навязчивое решение, которое я нашел, было бы переопределить
DB::postponed
. Это вызывается перед оценкой скомпилированного необходимого файла. К сожалению, он вызывается только при отладке (perl -d
).Другое решение состоит в том, чтобы прочитать файл, изменить его и оценить измененный файл, как показано ниже:
Вышеуказанное не правильно установлено
%INC
, оно портит имя файла, используемое в предупреждениях и т. Д., Оно не вызываетDB::postponed
и т. Д. Следующее является более надежным решением:Я использовал
UNITCHECK
(который вызывается после компиляции, но перед выполнением), потому что я добавлял переопределение (использованиеunread
), а не считывал весь файл и добавлял новое определение. Если вы хотите использовать этот подход, вы можете получить дескриптор файла, чтобы вернуться с помощьюПрестижность @Grinnz для упоминания
@INC
крючков.источник
Так как единственные опции здесь будут очень хакерскими, мы действительно хотим запустить код после добавления подпрограммы в
%Foo::
тайник:источник
Это выдаст некоторые предупреждения, но напечатает 7:
Сначала определимся
Foo::bar
. Это значение будет переопределено объявлением в Foo.pm, но сработает предупреждение «Подпрограмма Foo :: bar redefined», которое вызовет обработчик сигнала, который переопределяет подпрограмму снова, чтобы вернуть 7.источник
perl -w
.Вот решение, которое объединяет перехват процесса загрузки модуля с возможностями чтения только для модуля Readonly:
источник
Я пересмотрел свое решение здесь, чтобы оно больше не зависело от
Readonly.pm
, узнав, что я упустил очень простую альтернативу, основанную на ответе m-conrad , которую я переработал в модульный подход, который я начал здесь.Foo.pm ( То же, что и во вступительном посте )
OverrideSubs.pm Обновлено
test-run.pl
Запуск и вывод:
источник
Если
sub bar
внутриFoo.pm
есть прототип, отличный от существующейFoo::bar
функции, Perl не перезапишет его? Это, кажется, имеет место, и делает решение довольно простым:или вроде того же самого
Обновление: нет, причина, по которой это работает, заключается в том, что Perl не будет переопределять подпрограмму «константа» (с прототипом
()
), поэтому это только жизнеспособное решение, если ваша фиктивная функция постоянна.источник
BEGIN { *Foo::bar = sub () { 7 } }
лучше записать в видеsub Foo::bar() { 7 }
sub bar { 42 } my $baz = bar();
вместо общегоmy $baz = bar(); sub bar { 42 }
, это не сработало бы.Prototype mismatch: sub Foo::bar () vs none at Foo.pm line 5.
иConstant subroutine bar redefined at Foo.pm line 5.
)Давайте соревноваться в гольф!
Это просто префикс кода модуля с заменой метода, который будет первой строкой кода, которая выполняется после фазы компиляции и перед фазой выполнения.
Затем заполните
%INC
запись, чтобы в будущемuse Foo
не загружать оригинал.источник